From 92267b853afffa2f6ba0fde1a89d490ab8e6122e Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 13 Aug 2020 17:29:23 -0400 Subject: [PATCH 01/20] Merge POC into consul-k8s Signed-off-by: Ashwin Venkatesh --- api/v1alpha1/groupversion_info.go | 36 ++ api/v1alpha1/servicedefaults_types.go | 116 ++++++ api/v1alpha1/servicedefaults_webhook.go | 92 +++++ api/v1alpha1/status.go | 89 +++++ api/v1alpha1/types.go | 58 +++ api/v1alpha1/zz_generated.deepcopy.go | 226 ++++++++++++ config/certmanager/certificate.yaml | 26 ++ config/certmanager/kustomization.yaml | 5 + config/certmanager/kustomizeconfig.yaml | 16 + .../consul.hashicorp.com_servicedefaults.yaml | 143 ++++++++ config/crd/kustomization.yaml | 21 ++ config/crd/kustomizeconfig.yaml | 17 + .../cainjection_in_servicedefaults.yaml | 8 + .../patches/webhook_in_servicedefaults.yaml | 19 + config/default/kustomization.yaml | 74 ++++ config/default/manager_auth_proxy_patch.yaml | 25 ++ config/default/manager_webhook_patch.yaml | 23 ++ config/default/webhookcainjection_patch.yaml | 15 + config/manager/kustomization.yaml | 9 + config/manager/manager.yaml | 47 +++ config/prometheus/kustomization.yaml | 2 + config/prometheus/monitor.yaml | 16 + .../rbac/auth_proxy_client_clusterrole.yaml | 7 + config/rbac/auth_proxy_role.yaml | 13 + config/rbac/auth_proxy_role_binding.yaml | 12 + config/rbac/auth_proxy_service.yaml | 14 + config/rbac/kustomization.yaml | 12 + config/rbac/leader_election_role.yaml | 33 ++ config/rbac/leader_election_role_binding.yaml | 12 + config/rbac/role.yaml | 28 ++ config/rbac/role_binding.yaml | 12 + config/rbac/servicedefaults_editor_role.yaml | 24 ++ config/rbac/servicedefaults_viewer_role.yaml | 20 ++ .../consul_v1alpha1_servicedefaults.yaml | 6 + config/samples/kustomization.yaml | 3 + config/webhook/kustomization.yaml | 6 + config/webhook/kustomizeconfig.yaml | 25 ++ config/webhook/manifests.yaml | 52 +++ config/webhook/service.yaml | 12 + controller.go | 261 ++++++++++++++ controllers/servicedefaults_controller.go | 230 ++++++++++++ .../servicedefaults_controller_test.go | 331 ++++++++++++++++++ controllers/suite_test.go | 82 +++++ go.mod | 7 +- go.sum | 265 +++++++++++++- hack/boilerplate.go.txt | 15 + 46 files changed, 2557 insertions(+), 8 deletions(-) create mode 100644 api/v1alpha1/groupversion_info.go create mode 100644 api/v1alpha1/servicedefaults_types.go create mode 100644 api/v1alpha1/servicedefaults_webhook.go create mode 100644 api/v1alpha1/status.go create mode 100644 api/v1alpha1/types.go create mode 100644 api/v1alpha1/zz_generated.deepcopy.go create mode 100644 config/certmanager/certificate.yaml create mode 100644 config/certmanager/kustomization.yaml create mode 100644 config/certmanager/kustomizeconfig.yaml create mode 100644 config/crd/bases/consul.hashicorp.com_servicedefaults.yaml create mode 100644 config/crd/kustomization.yaml create mode 100644 config/crd/kustomizeconfig.yaml create mode 100644 config/crd/patches/cainjection_in_servicedefaults.yaml create mode 100644 config/crd/patches/webhook_in_servicedefaults.yaml create mode 100644 config/default/kustomization.yaml create mode 100644 config/default/manager_auth_proxy_patch.yaml create mode 100644 config/default/manager_webhook_patch.yaml create mode 100644 config/default/webhookcainjection_patch.yaml create mode 100644 config/manager/kustomization.yaml create mode 100644 config/manager/manager.yaml create mode 100644 config/prometheus/kustomization.yaml create mode 100644 config/prometheus/monitor.yaml create mode 100644 config/rbac/auth_proxy_client_clusterrole.yaml create mode 100644 config/rbac/auth_proxy_role.yaml create mode 100644 config/rbac/auth_proxy_role_binding.yaml create mode 100644 config/rbac/auth_proxy_service.yaml create mode 100644 config/rbac/kustomization.yaml create mode 100644 config/rbac/leader_election_role.yaml create mode 100644 config/rbac/leader_election_role_binding.yaml create mode 100644 config/rbac/role.yaml create mode 100644 config/rbac/role_binding.yaml create mode 100644 config/rbac/servicedefaults_editor_role.yaml create mode 100644 config/rbac/servicedefaults_viewer_role.yaml create mode 100644 config/samples/consul_v1alpha1_servicedefaults.yaml create mode 100644 config/samples/kustomization.yaml create mode 100644 config/webhook/kustomization.yaml create mode 100644 config/webhook/kustomizeconfig.yaml create mode 100644 config/webhook/manifests.yaml create mode 100644 config/webhook/service.yaml create mode 100644 controller.go create mode 100644 controllers/servicedefaults_controller.go create mode 100644 controllers/servicedefaults_controller_test.go create mode 100644 controllers/suite_test.go create mode 100644 hack/boilerplate.go.txt diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go new file mode 100644 index 0000000000..d375cb1154 --- /dev/null +++ b/api/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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 consul.hashicorp.com v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=consul.hashicorp.com +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "consul.hashicorp.com", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go new file mode 100644 index 0000000000..f80e8ea15d --- /dev/null +++ b/api/v1alpha1/servicedefaults_types.go @@ -0,0 +1,116 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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 ( + consulapi "github.com/hashicorp/consul/api" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ServiceDefaultsSpec defines the desired state of ServiceDefaults +type ServiceDefaultsSpec struct { + Protocol string `json:"protocol,omitempty"` + MeshGateway MeshGatewayConfig `json:"meshGateway,omitempty"` + Expose ExposeConfig `json:"expose,omitempty"` + ExternalSNI string `json:"externalSNI,omitempty"` +} + +// ServiceDefaultsStatus defines the observed state of ServiceDefaults +type ServiceDefaultsStatus struct { + Status `json:",inline"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// ServiceDefaults is the Schema for the servicedefaults API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +type ServiceDefaults struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServiceDefaultsSpec `json:"spec,omitempty"` + Status ServiceDefaultsStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ServiceDefaultsList contains a list of ServiceDefaults +type ServiceDefaultsList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceDefaults `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ServiceDefaults{}, &ServiceDefaultsList{}) +} + +func (s *ServiceDefaults) ToConsul() *consulapi.ServiceConfigEntry { + return &consulapi.ServiceConfigEntry{ + Kind: consulapi.ServiceDefaults, + Name: s.Name, + //Namespace: s.Namespace, // todo: don't set this unless enterprise + Protocol: s.Spec.Protocol, + MeshGateway: consulapi.MeshGatewayConfig{ + Mode: consulapi.MeshGatewayModeDefault, //this will change. forcing it to default for now. + }, + Expose: consulapi.ExposeConfig{ + Checks: s.Spec.Expose.Checks, + Paths: []consulapi.ExposePath{}, //will create a helper on our expose paths to translate to consul expose paths + }, + ExternalSNI: s.Spec.ExternalSNI, + } +} + +// this will check if the consul struct shares the same spec as the spec of the resource +func (s *ServiceDefaults) MatchesConsul(entry *consulapi.ServiceConfigEntry) bool { + matches := s.Name == entry.GetName() && + s.Spec.Protocol == entry.Protocol && + s.Spec.MeshGateway.Mode == string(entry.MeshGateway.Mode) && + s.Spec.Expose.Checks == entry.Expose.Checks && + s.Spec.ExternalSNI == entry.ExternalSNI + if !matches { + return false + } + + // Also check each exposed path config. + if len(s.Spec.Expose.Paths) != len(entry.Expose.Paths) { + return false + } + for _, path := range s.Spec.Expose.Paths { + found := false + for _, entryPath := range entry.Expose.Paths { + if path.ParsedFromCheck == entryPath.ParsedFromCheck && + path.Protocol == entryPath.Protocol && + path.Path == entryPath.Path && + path.ListenerPort == entryPath.ListenerPort && + path.LocalPathPort == entryPath.LocalPathPort { + found = true + break + } + } + + if !found { + return false + } + } + return true +} diff --git a/api/v1alpha1/servicedefaults_webhook.go b/api/v1alpha1/servicedefaults_webhook.go new file mode 100644 index 0000000000..465ae79a87 --- /dev/null +++ b/api/v1alpha1/servicedefaults_webhook.go @@ -0,0 +1,92 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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 ( + "context" + "fmt" + + "github.com/hashicorp/consul/api" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// log is for logging in this package. +var servicedefaultslog = logf.Log.WithName("servicedefaults-resource") + +// todo: use our own validating webhook so we can inject this properly +var ConsulClient *api.Client +var KubeClient client.Client + +func (r *ServiceDefaults) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// +kubebuilder:webhook:path=/mutate-consul-hashicorp-com-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,verbs=create;update,versions=v1alpha1,name=mservicedefaults.kb.io + +var _ webhook.Defaulter = &ServiceDefaults{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *ServiceDefaults) Default() { + servicedefaultslog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +// +kubebuilder:webhook:verbs=create;update,path=/validate-consul-hashicorp-com-v1alpha1-servicedefaults,mutating=false,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=vservicedefaults.kb.io + +var _ webhook.Validator = &ServiceDefaults{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *ServiceDefaults) ValidateCreate() error { + servicedefaultslog.Info("validate create", "name", r.Name) + var svcDefaultsList ServiceDefaultsList + if err := KubeClient.List(context.Background(), &svcDefaultsList); err != nil { + return err + } + for _, item := range svcDefaultsList.Items { + if item.Name == r.Name { + return fmt.Errorf("ServiceDefaults resource with name %q is already defined in namespace %q – all ServiceDefaults resources must have unique names across namespaces", + r.Name, item.Namespace) + } + } + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *ServiceDefaults) ValidateUpdate(old runtime.Object) error { + servicedefaultslog.Info("validate update", "name", r.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *ServiceDefaults) ValidateDelete() error { + servicedefaultslog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil +} diff --git a/api/v1alpha1/status.go b/api/v1alpha1/status.go new file mode 100644 index 0000000000..7f06f57b4d --- /dev/null +++ b/api/v1alpha1/status.go @@ -0,0 +1,89 @@ +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Conditions is the schema for the conditions portion of the payload +type Conditions []Condition + +// ConditionType is a camel-cased condition type. +type ConditionType string + +const ( + // ConditionSynced specifies that the resource has been synced with Consul. + ConditionSynced ConditionType = "Synced" +) + +// Conditions define a readiness condition for a Consul resource. +// See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true +type Condition struct { + // Type of condition. + // +required + Type ConditionType `json:"type" description:"type of status condition"` + + // Status of the condition, one of True, False, Unknown. + // +required + Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` + + // LastTransitionTime is the last time the condition transitioned from one status to another. + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transitioned from one status to another"` + + // The reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` + + // A human readable message indicating details about the transition. + // +optional + Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` +} + +// IsTrue is true if the condition is True +func (c *Condition) IsTrue() bool { + if c == nil { + return false + } + return c.Status == corev1.ConditionTrue +} + +// IsFalse is true if the condition is False +func (c *Condition) IsFalse() bool { + if c == nil { + return false + } + return c.Status == corev1.ConditionFalse +} + +// IsUnknown is true if the condition is Unknown +func (c *Condition) IsUnknown() bool { + if c == nil { + return true + } + return c.Status == corev1.ConditionUnknown +} + +// Status shows how we expect folks to embed Conditions in +// their Status field. +// WARNING: Adding fields to this struct will add them to all Consul-k8s resources. +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true +type Status struct { + // Conditions indicate the latest available observations of a resource's current state. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +func (s *Status) GetCondition(t ConditionType) *Condition { + for _, cond := range s.Conditions { + if cond.Type == t { + return &cond + } + } + return nil +} diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go new file mode 100644 index 0000000000..2f1a433e62 --- /dev/null +++ b/api/v1alpha1/types.go @@ -0,0 +1,58 @@ +package v1alpha1 + +type MeshGatewayMode string + +const ( + // MeshGatewayModeDefault represents no specific mode and should + // be used to indicate that a different layer of the configuration + // chain should take precedence + MeshGatewayModeDefault MeshGatewayMode = "" + + // MeshGatewayModeNone represents that the Upstream Connect connections + // should be direct and not flow through a mesh gateway. + MeshGatewayModeNone MeshGatewayMode = "none" + + // MeshGatewayModeLocal represents that the Upstrea Connect connections + // should be made to a mesh gateway in the local datacenter. This is + MeshGatewayModeLocal MeshGatewayMode = "local" + + // MeshGatewayModeRemote represents that the Upstream Connect connections + // should be made to a mesh gateway in a remote datacenter. + MeshGatewayModeRemote MeshGatewayMode = "remote" +) + +// MeshGatewayConfig controls how Mesh Gateways are used for upstream Connect +// services +type MeshGatewayConfig struct { + // Mode is the mode that should be used for the upstream connection. + Mode string `json:"mode,omitempty"` +} + +// ExposeConfig describes HTTP paths to expose through Envoy outside of Connect. +// Users can expose individual paths and/or all HTTP/GRPC paths for checks. +type ExposeConfig struct { + // Checks defines whether paths associated with Consul checks will be exposed. + // This flag triggers exposing all HTTP and GRPC check paths registered for the service. + Checks bool `json:"checks,omitempty"` + + // Paths is the list of paths exposed through the proxy. + Paths []ExposePath `json:"paths,omitempty"` +} + +type ExposePath struct { + // ListenerPort defines the port of the proxy's listener for exposed paths. + ListenerPort int `json:"listenerPort,omitempty"` + + // Path is the path to expose through the proxy, ie. "/metrics." + Path string `json:"path,omitempty"` + + // LocalPathPort is the port that the service is listening on for the given path. + LocalPathPort int `json:"localPathPort,omitempty"` + + // Protocol describes the upstream's service protocol. + // Valid values are "http" and "http2", defaults to "http" + Protocol string `json:"protocol,omitempty"` + + // ParsedFromCheck is set if this path was parsed from a registered check + ParsedFromCheck bool `json:"parsedFromCheck,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..8347a745a4 --- /dev/null +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,226 @@ +// +build !ignore_autogenerated + +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT 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 controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// 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 + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Conditions) DeepCopyInto(out *Conditions) { + { + in := &in + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Conditions. +func (in Conditions) DeepCopy() Conditions { + if in == nil { + return nil + } + out := new(Conditions) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExposeConfig) DeepCopyInto(out *ExposeConfig) { + *out = *in + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]ExposePath, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExposeConfig. +func (in *ExposeConfig) DeepCopy() *ExposeConfig { + if in == nil { + return nil + } + out := new(ExposeConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExposePath) DeepCopyInto(out *ExposePath) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExposePath. +func (in *ExposePath) DeepCopy() *ExposePath { + if in == nil { + return nil + } + out := new(ExposePath) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MeshGatewayConfig) DeepCopyInto(out *MeshGatewayConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshGatewayConfig. +func (in *MeshGatewayConfig) DeepCopy() *MeshGatewayConfig { + if in == nil { + return nil + } + out := new(MeshGatewayConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceDefaults) DeepCopyInto(out *ServiceDefaults) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDefaults. +func (in *ServiceDefaults) DeepCopy() *ServiceDefaults { + if in == nil { + return nil + } + out := new(ServiceDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceDefaults) 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 *ServiceDefaultsList) DeepCopyInto(out *ServiceDefaultsList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceDefaults, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDefaultsList. +func (in *ServiceDefaultsList) DeepCopy() *ServiceDefaultsList { + if in == nil { + return nil + } + out := new(ServiceDefaultsList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceDefaultsList) 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 *ServiceDefaultsSpec) DeepCopyInto(out *ServiceDefaultsSpec) { + *out = *in + out.MeshGateway = in.MeshGateway + in.Expose.DeepCopyInto(&out.Expose) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDefaultsSpec. +func (in *ServiceDefaultsSpec) DeepCopy() *ServiceDefaultsSpec { + if in == nil { + return nil + } + out := new(ServiceDefaultsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceDefaultsStatus) DeepCopyInto(out *ServiceDefaultsStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDefaultsStatus. +func (in *ServiceDefaultsStatus) DeepCopy() *ServiceDefaultsStatus { + if in == nil { + return nil + } + out := new(ServiceDefaultsStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Status) DeepCopyInto(out *Status) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status. +func (in *Status) DeepCopy() *Status { + if in == nil { + return nil + } + out := new(Status) + in.DeepCopyInto(out) + return out +} diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml new file mode 100644 index 0000000000..58db114fa0 --- /dev/null +++ b/config/certmanager/certificate.yaml @@ -0,0 +1,26 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for +# breaking changes +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml new file mode 100644 index 0000000000..bebea5a595 --- /dev/null +++ b/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 0000000000..90d7c313ca --- /dev/null +++ b/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml new file mode 100644 index 0000000000..4cbcf52ab6 --- /dev/null +++ b/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -0,0 +1,143 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: servicedefaults.consul.hashicorp.com +spec: + additionalPrinterColumns: + - JSONPath: .status.conditions[?(@.type=="Synced")].status + description: The sync status of the resource with Consul + name: Synced + type: string + group: consul.hashicorp.com + names: + kind: ServiceDefaults + listKind: ServiceDefaultsList + plural: servicedefaults + singular: servicedefaults + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: ServiceDefaults is the Schema for the servicedefaults API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServiceDefaultsSpec defines the desired state of ServiceDefaults + properties: + expose: + description: ExposeConfig describes HTTP paths to expose through Envoy + outside of Connect. Users can expose individual paths and/or all HTTP/GRPC + paths for checks. + properties: + checks: + description: Checks defines whether paths associated with Consul + checks will be exposed. This flag triggers exposing all HTTP and + GRPC check paths registered for the service. + type: boolean + paths: + description: Paths is the list of paths exposed through the proxy. + items: + properties: + listenerPort: + description: ListenerPort defines the port of the proxy's + listener for exposed paths. + type: integer + localPathPort: + description: LocalPathPort is the port that the service is + listening on for the given path. + type: integer + parsedFromCheck: + description: ParsedFromCheck is set if this path was parsed + from a registered check + type: boolean + path: + description: Path is the path to expose through the proxy, + ie. "/metrics." + type: string + protocol: + description: Protocol describes the upstream's service protocol. + Valid values are "http" and "http2", defaults to "http" + type: string + type: object + type: array + type: object + externalSNI: + type: string + meshGateway: + description: MeshGatewayConfig controls how Mesh Gateways are used for + upstream Connect services + properties: + mode: + description: Mode is the mode that should be used for the upstream + connection. + type: string + type: object + protocol: + type: string + type: object + status: + description: ServiceDefaultsStatus defines the observed state of ServiceDefaults + properties: + conditions: + description: Conditions indicate the latest available observations of + a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true + - name: v1alpha1 + served: true + storage: false +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml new file mode 100644 index 0000000000..6ee500b452 --- /dev/null +++ b/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/consul.hashicorp.com_servicedefaults.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +- patches/webhook_in_servicedefaults.yaml +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +- patches/cainjection_in_servicedefaults.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000000..6f83d9a94b --- /dev/null +++ b/config/crd/kustomizeconfig.yaml @@ -0,0 +1,17 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/config/crd/patches/cainjection_in_servicedefaults.yaml b/config/crd/patches/cainjection_in_servicedefaults.yaml new file mode 100644 index 0000000000..3ae80de8ce --- /dev/null +++ b/config/crd/patches/cainjection_in_servicedefaults.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: servicedefaults.consul.hashicorp.com diff --git a/config/crd/patches/webhook_in_servicedefaults.yaml b/config/crd/patches/webhook_in_servicedefaults.yaml new file mode 100644 index 0000000000..9b7609abb8 --- /dev/null +++ b/config/crd/patches/webhook_in_servicedefaults.yaml @@ -0,0 +1,19 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: servicedefaults.consul.hashicorp.com +spec: + conversion: + # todo: this wasn't working with my kube version + strategy: None +# strategy: Webhook +# webhookClientConfig: +# # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, +# # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) +# caBundle: Cg== +# service: +# namespace: system +# name: webhook-service +# path: /convert diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml new file mode 100644 index 0000000000..df978482e5 --- /dev/null +++ b/config/default/kustomization.yaml @@ -0,0 +1,74 @@ +# Adds namespace to all resources. +namespace: default + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: consul-controller- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + + # Protect the /metrics endpoint by putting it behind auth. + # If you want your controller-manager to expose the /metrics + # endpoint w/o any authn/z, please comment the following line. + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +patchesStrategicMerge: +- manager_auth_proxy_patch.yaml +- manager_webhook_patch.yaml +- webhookcainjection_patch.yaml + +# the following config is for teaching kustomize how to do var substitution +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +vars: +- fieldref: + fieldPath: metadata.namespace + name: CERTIFICATE_NAMESPACE + objref: + group: cert-manager.io + kind: Certificate + name: serving-cert + version: v1alpha2 +- fieldref: {} + name: CERTIFICATE_NAME + objref: + group: cert-manager.io + kind: Certificate + name: serving-cert + version: v1alpha2 +- fieldref: + fieldPath: metadata.namespace + name: SERVICE_NAMESPACE + objref: + kind: Service + name: webhook-service + version: v1 +- fieldref: {} + name: SERVICE_NAME + objref: + kind: Service + name: webhook-service + version: v1 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- ../crd +- ../rbac +- ../manager +- ../webhook +- ../certmanager diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 0000000000..77e743d1c1 --- /dev/null +++ b/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,25 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml new file mode 100644 index 0000000000..738de350b7 --- /dev/null +++ b/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml new file mode 100644 index 0000000000..7e79bf9955 --- /dev/null +++ b/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,15 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml new file mode 100644 index 0000000000..5e2008f85e --- /dev/null +++ b/config/manager/kustomization.yaml @@ -0,0 +1,9 @@ +resources: +- manager.yaml +# todo: this was auto-generated +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: lkysow/consul-k8s-controller + newTag: jul23 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml new file mode 100644 index 0000000000..5e01a00a12 --- /dev/null +++ b/config/manager/manager.yaml @@ -0,0 +1,47 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - name: manager + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + command: + - /manager + args: + - --enable-leader-election + image: controller:latest + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + + terminationGracePeriodSeconds: 10 diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml new file mode 100644 index 0000000000..ed137168a1 --- /dev/null +++ b/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml new file mode 100644 index 0000000000..9b8047b760 --- /dev/null +++ b/config/prometheus/monitor.yaml @@ -0,0 +1,16 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + selector: + matchLabels: + control-plane: controller-manager diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml new file mode 100644 index 0000000000..7d62534c5f --- /dev/null +++ b/config/rbac/auth_proxy_client_clusterrole.yaml @@ -0,0 +1,7 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: ["/metrics"] + verbs: ["get"] diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml new file mode 100644 index 0000000000..618f5e4177 --- /dev/null +++ b/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: proxy-role +rules: +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 0000000000..48ed1e4b85 --- /dev/null +++ b/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml new file mode 100644 index 0000000000..6cf656be14 --- /dev/null +++ b/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml new file mode 100644 index 0000000000..66c28338fe --- /dev/null +++ b/config/rbac/kustomization.yaml @@ -0,0 +1,12 @@ +resources: +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 4 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml +- auth_proxy_client_clusterrole.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml new file mode 100644 index 0000000000..7dc16c420e --- /dev/null +++ b/config/rbac/leader_election_role.yaml @@ -0,0 +1,33 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 0000000000..eed16906f4 --- /dev/null +++ b/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml new file mode 100644 index 0000000000..036cbf8d46 --- /dev/null +++ b/config/rbac/role.yaml @@ -0,0 +1,28 @@ + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults/status + verbs: + - get + - patch + - update diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml new file mode 100644 index 0000000000..8f2658702c --- /dev/null +++ b/config/rbac/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/config/rbac/servicedefaults_editor_role.yaml b/config/rbac/servicedefaults_editor_role.yaml new file mode 100644 index 0000000000..f5a7d27a26 --- /dev/null +++ b/config/rbac/servicedefaults_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit servicedefaults. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: servicedefaults-editor-role +rules: +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults/status + verbs: + - get diff --git a/config/rbac/servicedefaults_viewer_role.yaml b/config/rbac/servicedefaults_viewer_role.yaml new file mode 100644 index 0000000000..ac9ccc9b3b --- /dev/null +++ b/config/rbac/servicedefaults_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view servicedefaults. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: servicedefaults-viewer-role +rules: +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults + verbs: + - get + - list + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults/status + verbs: + - get diff --git a/config/samples/consul_v1alpha1_servicedefaults.yaml b/config/samples/consul_v1alpha1_servicedefaults.yaml new file mode 100644 index 0000000000..f395256b81 --- /dev/null +++ b/config/samples/consul_v1alpha1_servicedefaults.yaml @@ -0,0 +1,6 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceDefaults +metadata: + name: servicedefaults-sample +spec: + protocol: "http" diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml new file mode 100644 index 0000000000..c6cf924560 --- /dev/null +++ b/config/samples/kustomization.yaml @@ -0,0 +1,3 @@ +## This file is auto-generated, do not modify ## +resources: +- consul_v1alpha1_servicedefaults.yaml diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 0000000000..9cf26134e4 --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 0000000000..25e21e3c96 --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml new file mode 100644 index 0000000000..00639010ba --- /dev/null +++ b/config/webhook/manifests.yaml @@ -0,0 +1,52 @@ + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: mutating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: webhook-service + namespace: system + path: /mutate-consul-hashicorp-com-v1alpha1-servicedefaults + failurePolicy: Fail + name: mservicedefaults.kb.io + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicedefaults + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: webhook-service + namespace: system + path: /validate-consul-hashicorp-com-v1alpha1-servicedefaults + failurePolicy: Fail + name: vservicedefaults.kb.io + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicedefaults diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 0000000000..31e0f82959 --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/controller.go b/controller.go new file mode 100644 index 0000000000..f517ae010c --- /dev/null +++ b/controller.go @@ -0,0 +1,261 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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" + "io/ioutil" + "os" + "strings" + + "github.com/hashicorp/consul/api" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/hashicorp/consul-k8s/api/v1alpha1" + "github.com/hashicorp/consul-k8s/controllers" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(v1alpha1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + +// +//func main() { +// os.Exit(realMain()) +//} + +func realMain() int { + var metricsAddr string + var enableLeaderElection bool + flags := flag.NewFlagSet("", flag.ContinueOnError) + flags.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + flags.BoolVar(&enableLeaderElection, "enable-leader-election", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + httpFlags := HTTPFlags{} + Merge(flags, httpFlags.Flags()) + if err := flags.Parse(nil); err != nil { + setupLog.Error(err, "parsing flags") + return 1 + } + + ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + Port: 9443, + LeaderElection: enableLeaderElection, + LeaderElectionID: "65a0bb41.my.domain", + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + return 1 + } + + consulClient, err := httpFlags.APIClient() + if err != nil { + setupLog.Error(err, "connecting to Consul agent") + return 1 + } + + if err = (&controllers.ServiceDefaultsReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("ServiceDefaults"), + Scheme: mgr.GetScheme(), + ConsulClient: consulClient, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ServiceDefaults") + return 1 + } + // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. + // Instead we should implement our own validating webhooks so we can pass in the clients. + v1alpha1.ConsulClient = consulClient + v1alpha1.KubeClient = mgr.GetClient() + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err = (&v1alpha1.ServiceDefaults{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ServiceDefaults") + os.Exit(1) + } + } + // +kubebuilder:scaffold:builder + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + return 1 + } + return 0 +} + +// Taken from https://github.com/hashicorp/consul/blob/b5b9c8d953cd3c79c6b795946839f4cf5012f507/command/flags/http.go +// with flags we don't use removed. This was done so we don't depend on internal +// Consul implementation. + +// HTTPFlags are flags used to configure communication with a Consul agent. +type HTTPFlags struct { + address StringValue + token StringValue + tokenFile StringValue + caFile StringValue + caPath StringValue + certFile StringValue + keyFile StringValue + tlsServerName StringValue +} + +func (f *HTTPFlags) Flags() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.Var(&f.address, "http-addr", + "The `address` and port of the Consul HTTP agent. The value can be an IP "+ + "address or DNS address, but it must also include the port. This can "+ + "also be specified via the CONSUL_HTTP_ADDR environment variable. The "+ + "default value is http://127.0.0.1:8500. The scheme can also be set to "+ + "HTTPS by setting the environment variable CONSUL_HTTP_SSL=true.") + fs.Var(&f.token, "token", + "ACL token to use in the request. This can also be specified via the "+ + "CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+ + "default to the token of the Consul agent at the HTTP address.") + fs.Var(&f.tokenFile, "token-file", + "File containing the ACL token to use in the request instead of one specified "+ + "via the -token argument or CONSUL_HTTP_TOKEN environment variable. "+ + "This can also be specified via the CONSUL_HTTP_TOKEN_FILE environment variable.") + fs.Var(&f.caFile, "ca-file", + "Path to a CA file to use for TLS when communicating with Consul. This "+ + "can also be specified via the CONSUL_CACERT environment variable.") + fs.Var(&f.caPath, "ca-path", + "Path to a directory of CA certificates to use for TLS when communicating "+ + "with Consul. This can also be specified via the CONSUL_CAPATH environment variable.") + fs.Var(&f.certFile, "client-cert", + "Path to a client cert file to use for TLS when 'verify_incoming' is enabled. This "+ + "can also be specified via the CONSUL_CLIENT_CERT environment variable.") + fs.Var(&f.keyFile, "client-key", + "Path to a client key file to use for TLS when 'verify_incoming' is enabled. This "+ + "can also be specified via the CONSUL_CLIENT_KEY environment variable.") + fs.Var(&f.tlsServerName, "tls-server-name", + "The server name to use as the SNI host when connecting via TLS. This "+ + "can also be specified via the CONSUL_TLS_SERVER_NAME environment variable.") + return fs +} + +func (f *HTTPFlags) Addr() string { + return f.address.String() +} + +func (f *HTTPFlags) Token() string { + return f.token.String() +} + +func (f *HTTPFlags) SetToken(v string) error { + return f.token.Set(v) +} + +func (f *HTTPFlags) TokenFile() string { + return f.tokenFile.String() +} + +func (f *HTTPFlags) SetTokenFile(v string) error { + return f.tokenFile.Set(v) +} + +func (f *HTTPFlags) ReadTokenFile() (string, error) { + tokenFile := f.tokenFile.String() + if tokenFile == "" { + return "", nil + } + + data, err := ioutil.ReadFile(tokenFile) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(data)), nil +} + +func (f *HTTPFlags) APIClient() (*api.Client, error) { + c := api.DefaultConfig() + + f.MergeOntoConfig(c) + + return api.NewClient(c) +} + +func (f *HTTPFlags) MergeOntoConfig(c *api.Config) { + f.address.Merge(&c.Address) + f.token.Merge(&c.Token) + f.tokenFile.Merge(&c.TokenFile) + f.caFile.Merge(&c.TLSConfig.CAFile) + f.caPath.Merge(&c.TLSConfig.CAPath) + f.certFile.Merge(&c.TLSConfig.CertFile) + f.keyFile.Merge(&c.TLSConfig.KeyFile) + f.tlsServerName.Merge(&c.TLSConfig.Address) +} + +func Merge(dst, src *flag.FlagSet) { + if dst == nil { + panic("dst cannot be nil") + } + if src == nil { + return + } + src.VisitAll(func(f *flag.Flag) { + dst.Var(f.Value, f.Name, f.Usage) + }) +} + +// StringValue provides a flag value that's aware if it has been set. +type StringValue struct { + v *string +} + +// Merge will overlay this value if it has been set. +func (s *StringValue) Merge(onto *string) { + if s.v != nil { + *onto = *(s.v) + } +} + +// Set implements the flag.Value interface. +func (s *StringValue) Set(v string) error { + if s.v == nil { + s.v = new(string) + } + *(s.v) = v + return nil +} + +// String implements the flag.Value interface. +func (s *StringValue) String() string { + var current string + if s.v != nil { + current = *(s.v) + } + return current +} diff --git a/controllers/servicedefaults_controller.go b/controllers/servicedefaults_controller.go new file mode 100644 index 0000000000..1c088a115d --- /dev/null +++ b/controllers/servicedefaults_controller.go @@ -0,0 +1,230 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "errors" + "strings" + + "github.com/go-logr/logr" + capi "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + k8serr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + consulv1alpha1 "github.com/hashicorp/consul-k8s/api/v1alpha1" +) + +const ( + FinalizerName = "finalizers.consul.hashicorp.com" + ConsulAgentError = "ConsulAgentError" + CastError = "CastError" +) + +// ServiceDefaultsReconciler reconciles a ServiceDefaults object +type ServiceDefaultsReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + ConsulClient *capi.Client +} + +// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=servicedefaults,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=servicedefaults/status,verbs=get;update;patch + +func (r *ServiceDefaultsReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + ctx := context.Background() + logger := r.Log.WithValues("servicedefaults", req.NamespacedName) + var svcDefaults consulv1alpha1.ServiceDefaults + + err := r.Get(ctx, req.NamespacedName, &svcDefaults) + if k8serr.IsNotFound(err) { + return ctrl.Result{}, client.IgnoreNotFound(err) + } else if err != nil { + logger.Error(err, "failed to retrieve Service Default") + return ctrl.Result{}, err + } + + if svcDefaults.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted, so if it does not have our finalizer, + // then lets add the finalizer and update the object. This is equivalent + // registering our finalizer. + if !containsString(svcDefaults.ObjectMeta.Finalizers, FinalizerName) { + svcDefaults.ObjectMeta.Finalizers = append(svcDefaults.ObjectMeta.Finalizers, FinalizerName) + svcDefaults.Status.Conditions = syncUnknown() + if err := r.Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + } + } else { + // The object is being deleted + if containsString(svcDefaults.ObjectMeta.Finalizers, FinalizerName) { + logger.Info("deletion event") + // our finalizer is present, so we need to delete the config entry + // from consul. + _, err = r.ConsulClient.ConfigEntries().Delete(capi.ServiceDefaults, svcDefaults.Name, nil) + if err != nil { + return ctrl.Result{}, err + } + logger.Info("deletion from Consul successful") + + // remove our finalizer from the list and update it. + svcDefaults.ObjectMeta.Finalizers = removeString(svcDefaults.ObjectMeta.Finalizers, FinalizerName) + if err := r.Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + logger.Info("finalizer removed") + } + + // Stop reconciliation as the item is being deleted + return ctrl.Result{}, nil + } + + // Check to see if consul has service defaults with the same name + entry, _, err := r.ConsulClient.ConfigEntries().Get(capi.ServiceDefaults, svcDefaults.Name, nil) + // If a config entry with this name does not exist + if isNotFoundErr(err) { + // Create the config entry + _, _, err := r.ConsulClient.ConfigEntries().Set(svcDefaults.ToConsul(), nil) + if err != nil { + svcDefaults.Status.Conditions = syncFailed(ConsulAgentError, err.Error()) + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + svcDefaults.Status.Conditions = syncSuccessful() + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + } else if err != nil { + svcDefaults.Status.Conditions = syncFailed(ConsulAgentError, err.Error()) + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } else { + svcDefaultEntry, ok := entry.(*capi.ServiceConfigEntry) + if !ok { + err := errors.New("could not cast entry as ServiceConfigEntry") + svcDefaults.Status.Conditions = syncUnknownWithError(CastError, err.Error()) + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + if !svcDefaults.MatchesConsul(svcDefaultEntry) { + _, _, err := r.ConsulClient.ConfigEntries().Set(svcDefaults.ToConsul(), nil) + if err != nil { + svcDefaults.Status.Conditions = syncUnknownWithError(ConsulAgentError, err.Error()) + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + svcDefaults.Status.Conditions = syncSuccessful() + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + } else if !svcDefaults.Status.GetCondition(consulv1alpha1.ConditionSynced).IsTrue() { + svcDefaults.Status.Conditions = syncSuccessful() + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + } + } + + return ctrl.Result{}, nil +} + +func (r *ServiceDefaultsReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&consulv1alpha1.ServiceDefaults{}). + Complete(r) +} + +func syncFailed(reason, message string) consulv1alpha1.Conditions { + return consulv1alpha1.Conditions{ + { + Type: consulv1alpha1.ConditionSynced, + Status: corev1.ConditionFalse, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func syncSuccessful() consulv1alpha1.Conditions { + return consulv1alpha1.Conditions{ + { + Type: consulv1alpha1.ConditionSynced, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.Now(), + }, + } +} + +func syncUnknown() consulv1alpha1.Conditions { + return consulv1alpha1.Conditions{ + { + Type: consulv1alpha1.ConditionSynced, + Status: corev1.ConditionUnknown, + LastTransitionTime: metav1.Now(), + }, + } +} + +func syncUnknownWithError(reason, message string) consulv1alpha1.Conditions { + return consulv1alpha1.Conditions{ + { + Type: consulv1alpha1.ConditionSynced, + Status: corev1.ConditionUnknown, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func isNotFoundErr(err error) bool { + return err != nil && strings.Contains(err.Error(), "404") +} + +// Helper functions to check and remove string from a slice of strings. +func containsString(slice []string, s string) bool { + for _, item := range slice { + if item == s { + return true + } + } + return false +} + +func removeString(slice []string, s string) (result []string) { + for _, item := range slice { + if item == s { + continue + } + result = append(result, item) + } + return +} diff --git a/controllers/servicedefaults_controller_test.go b/controllers/servicedefaults_controller_test.go new file mode 100644 index 0000000000..f6a560558a --- /dev/null +++ b/controllers/servicedefaults_controller_test.go @@ -0,0 +1,331 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers_test + +import ( + "context" + "testing" + "time" + + logrtest "github.com/go-logr/logr/testing" + "github.com/hashicorp/consul-k8s/api/v1alpha1" + "github.com/hashicorp/consul-k8s/controllers" + capi "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestServiceDefaultsController_createsConfigEntry(t *testing.T) { + t.SkipNow() + req := require.New(t) + svcDefaults := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + s := scheme.Scheme + s.AddKnownTypes(v1alpha1.GroupVersion, svcDefaults) + ctx := context.Background() + + consul, err := testutil.NewTestServerConfigT(t, nil) + req.NoError(err) + defer consul.Stop() + consulClient, err := capi.NewClient(&capi.Config{ + Address: consul.HTTPAddr, + }) + req.NoError(err) + + client := fake.NewFakeClientWithScheme(s, svcDefaults) + + r := controllers.ServiceDefaultsReconciler{ + Client: client, + Log: logrtest.NullLogger{}, + Scheme: s, + ConsulClient: consulClient, + } + + resp, err := r.Reconcile(ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: svcDefaults.ObjectMeta.Namespace, + Name: svcDefaults.ObjectMeta.Name, + }, + }) + req.NoError(err) + req.False(resp.Requeue) + + cfg, _, err := consulClient.ConfigEntries().Get(capi.ServiceDefaults, "foo", nil) + req.NoError(err) + svcDefault, ok := cfg.(*capi.ServiceConfigEntry) + req.True(ok) + req.Equal("http", svcDefault.Protocol) + + // Check that the status is "synced". + err = client.Get(ctx, types.NamespacedName{ + Namespace: svcDefaults.Namespace, + Name: svcDefaults.Name, + }, svcDefaults) + req.NoError(err) + conditionSynced := svcDefaults.Status.GetCondition(v1alpha1.ConditionSynced) + req.True(conditionSynced.IsTrue()) +} + +func TestServiceDefaultsController_addsFinalizerOnCreate(t *testing.T) { + req := require.New(t) + s := scheme.Scheme + svcDefaults := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + s.AddKnownTypes(v1alpha1.GroupVersion, svcDefaults) + + consul, err := testutil.NewTestServerConfigT(t, nil) + req.NoError(err) + defer consul.Stop() + consulClient, err := capi.NewClient(&capi.Config{ + Address: consul.HTTPAddr, + }) + req.NoError(err) + + client := fake.NewFakeClientWithScheme(s, svcDefaults) + + r := controllers.ServiceDefaultsReconciler{ + Client: client, + Log: logrtest.NullLogger{}, + Scheme: s, + ConsulClient: consulClient, + } + + resp, err := r.Reconcile(ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: svcDefaults.ObjectMeta.Namespace, + Name: svcDefaults.ObjectMeta.Name, + }, + }) + req.NoError(err) + req.False(resp.Requeue) + + err = client.Get(context.Background(), types.NamespacedName{ + Namespace: svcDefaults.Namespace, + Name: svcDefaults.Name, + }, svcDefaults) + req.NoError(err) + req.Contains(svcDefaults.Finalizers, controllers.FinalizerName) + conditionSynced := svcDefaults.Status.GetCondition(v1alpha1.ConditionSynced) + req.True(conditionSynced.IsTrue()) +} + +func TestServiceDefaultsController_updatesConfigEntry(t *testing.T) { + t.SkipNow() + req := require.New(t) + svcDefaults := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + Finalizers: []string{controllers.FinalizerName}, + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + s := scheme.Scheme + s.AddKnownTypes(v1alpha1.GroupVersion, svcDefaults) + ctx := context.Background() + + consul, err := testutil.NewTestServerConfigT(t, nil) + req.NoError(err) + defer consul.Stop() + consulClient, err := capi.NewClient(&capi.Config{ + Address: consul.HTTPAddr, + }) + req.NoError(err) + + client := fake.NewFakeClientWithScheme(s, svcDefaults) + + r := controllers.ServiceDefaultsReconciler{ + Client: client, + Log: logrtest.NullLogger{}, + Scheme: s, + ConsulClient: consulClient, + } + + // We haven't run reconcile yet so ensure it's created in Consul. + { + written, _, err := consulClient.ConfigEntries().Set(&capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "foo", + Protocol: "http", + }, nil) + req.NoError(err) + req.True(written) + } + + // Now update it. + { + // First get it so we have the latest revision number. + err = client.Get(ctx, types.NamespacedName{ + Namespace: svcDefaults.Namespace, + Name: svcDefaults.Name, + }, svcDefaults) + req.NoError(err) + + // Update the protocol. + svcDefaults.Spec.Protocol = "tcp" + err := client.Update(ctx, svcDefaults) + req.NoError(err) + + resp, err := r.Reconcile(ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: svcDefaults.ObjectMeta.Namespace, + Name: svcDefaults.ObjectMeta.Name, + }, + }) + req.NoError(err) + req.False(resp.Requeue) + + cfg, _, err := consulClient.ConfigEntries().Get(capi.ServiceDefaults, "foo", nil) + req.NoError(err) + svcDefault, ok := cfg.(*capi.ServiceConfigEntry) + req.True(ok) + req.Equal("tcp", svcDefault.Protocol) + } +} + +func TestServiceDefaultsController_deletesConfigEntry(t *testing.T) { + req := require.New(t) + // Create it with the deletion timestamp set to mimic that it's already + // been marked for deletion. + svcDefaults := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{controllers.FinalizerName}, + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + s := scheme.Scheme + s.AddKnownTypes(v1alpha1.GroupVersion, svcDefaults) + + consul, err := testutil.NewTestServerConfigT(t, nil) + req.NoError(err) + defer consul.Stop() + consulClient, err := capi.NewClient(&capi.Config{ + Address: consul.HTTPAddr, + }) + req.NoError(err) + + client := fake.NewFakeClientWithScheme(s, svcDefaults) + + r := controllers.ServiceDefaultsReconciler{ + Client: client, + Log: logrtest.NullLogger{}, + Scheme: s, + ConsulClient: consulClient, + } + + // We haven't run reconcile yet so ensure it's created in Consul. + { + written, _, err := consulClient.ConfigEntries().Set(&capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "foo", + Protocol: "http", + }, nil) + req.NoError(err) + req.True(written) + } + + // Now run reconcile. It's marked for deletion so this should delete it. + { + resp, err := r.Reconcile(ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: svcDefaults.ObjectMeta.Namespace, + Name: svcDefaults.ObjectMeta.Name, + }, + }) + req.NoError(err) + req.False(resp.Requeue) + + _, _, err = consulClient.ConfigEntries().Get(capi.ServiceDefaults, "foo", nil) + req.EqualError(err, "Unexpected response code: 404 (Config entry not found for \"service-defaults\" / \"foo\")") + } +} + +func TestServiceDefaultsController_errorUpdatesSyncStatus(t *testing.T) { + req := require.New(t) + svcDefaults := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + s := scheme.Scheme + s.AddKnownTypes(v1alpha1.GroupVersion, svcDefaults) + ctx := context.Background() + + consulClient, err := capi.NewClient(&capi.Config{ + Address: "incorrect-address", + }) + req.NoError(err) + + client := fake.NewFakeClientWithScheme(s, svcDefaults) + + r := controllers.ServiceDefaultsReconciler{ + Client: client, + Log: logrtest.NullLogger{}, + Scheme: s, + ConsulClient: consulClient, + } + + resp, err := r.Reconcile(ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: svcDefaults.Namespace, + Name: svcDefaults.Name, + }, + }) + req.EqualError(err, "Get \"http://incorrect-address/v1/config/service-defaults/foo\": dial tcp: lookup incorrect-address: no such host") + req.False(resp.Requeue) + + // Check that the status is "synced=false". + err = client.Get(ctx, types.NamespacedName{ + Namespace: svcDefaults.Namespace, + Name: svcDefaults.Name, + }, svcDefaults) + req.NoError(err) + conditionSynced := svcDefaults.Status.GetCondition(v1alpha1.ConditionSynced) + req.True(conditionSynced.IsFalse()) + req.Equal("ConsulAgentError", conditionSynced.Reason) + req.Equal("Get \"http://incorrect-address/v1/config/service-defaults/foo\": dial tcp: lookup incorrect-address: no such host", conditionSynced.Message) +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go new file mode 100644 index 0000000000..2c3980091d --- /dev/null +++ b/controllers/suite_test.go @@ -0,0 +1,82 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + consulv1alpha1 "github.com/hashicorp/consul-k8s/api/v1alpha1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + t.SkipNow() + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func(done Done) { + logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = consulv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/go.mod b/go.mod index 777b006f24..6f4032f8eb 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/cenkalti/backoff v2.1.1+incompatible github.com/deckarep/golang-set v1.7.1 github.com/digitalocean/godo v1.10.0 // indirect + github.com/go-logr/logr v0.1.0 github.com/hashicorp/consul/api v1.6.0 github.com/hashicorp/consul/sdk v0.6.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f @@ -13,13 +14,12 @@ require ( github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/imdario/mergo v0.3.8 // indirect github.com/kr/text v0.1.0 github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/onsi/gomega v1.8.1 // indirect + github.com/onsi/ginkgo v1.12.1 + github.com/onsi/gomega v1.10.1 github.com/radovskyb/watcher v1.0.2 github.com/stretchr/testify v1.4.0 golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect @@ -30,6 +30,7 @@ require ( k8s.io/api v0.18.2 k8s.io/apimachinery v0.18.2 k8s.io/client-go v0.18.2 + sigs.k8s.io/controller-runtime v0.6.0 ) go 1.14 diff --git a/go.sum b/go.sum index 08cdb22b2e..d6e2ec13ee 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible h1:e82Yv2HNpS0kuyeCrV29OPKvEiqfs2/uJHic3/3iKdg= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -39,11 +40,19 @@ github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= @@ -51,19 +60,37 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.25.41 h1:/hj7nZ0586wFqpwjNpzWiUTwtaMgxAZNZKHay80MdXw= github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -80,11 +107,22 @@ github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TR github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= @@ -92,24 +130,83 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= @@ -117,6 +214,9 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= @@ -125,6 +225,7 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -132,9 +233,16 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/consul/api v1.6.0 h1:SZB2hQW8AcTOpfDmiVblQbijxzsRuiyy0JpHfabvHio= github.com/hashicorp/consul/api v1.6.0/go.mod h1:1NSuaUUkFaJzMasbfq/11wKYWSR67Xn6r2DXKhuDNFg= github.com/hashicorp/consul/sdk v0.6.0 h1:FfhMEkwvQl57CildXJyGHnwGGM4HMODGyfjGwNM1Vdw= @@ -171,9 +279,9 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8= @@ -187,30 +295,44 @@ github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35n github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a h1:+J2gw7Bw77w/fbK7wnNJJDKmw1IbWft2Ul5BzrG1Qm8= github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -219,11 +341,13 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= @@ -249,22 +373,33 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -276,42 +411,101 @@ github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/radovskyb/watcher v1.0.2 h1:9L5TsZUbo1nKhQEQPtICVc+x9UZQ6VPdBepLHyGw/bQ= github.com/radovskyb/watcher v1.0.2/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= @@ -320,21 +514,29 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -348,22 +550,33 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -376,21 +589,32 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -402,38 +626,66 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8= +k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= +k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= @@ -442,6 +694,9 @@ k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDN k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM= +sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt new file mode 100644 index 0000000000..767efde981 --- /dev/null +++ b/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ \ No newline at end of file From 1a9c1c908139ee87319a0176cc75327c04f36df0 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 14 Aug 2020 14:39:43 -0400 Subject: [PATCH 02/20] Migrate controller to subcommand pattern - Update Makefile with some code-gen tasks --- Makefile | 39 +++ commands.go | 5 + .../servicedefaults_controller_test.go | 6 +- go.mod | 19 +- go.sum | 115 +++++++- subcommand/controller/command.go | 263 ++++++++++++++++++ 6 files changed, 426 insertions(+), 21 deletions(-) create mode 100644 subcommand/controller/command.go diff --git a/Makefile b/Makefile index 7594afcf5f..5e94a3f764 100644 --- a/Makefile +++ b/Makefile @@ -128,6 +128,45 @@ clean: $(CURDIR)/bin \ $(CURDIR)/pkg +# Generate manifests e.g. CRD, RBAC etc. +manifests: controller-gen + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +# Generate code +generate: controller-gen + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +# find or download controller-gen +# download controller-gen if necessary +controller-gen: +ifeq (, $(shell which controller-gen)) + @{ \ + set -e ;\ + CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ + cd $$CONTROLLER_GEN_TMP_DIR ;\ + go mod init tmp ;\ + go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.3.0 ;\ + rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ + } +CONTROLLER_GEN=$(GOBIN)/controller-gen +else +CONTROLLER_GEN=$(shell which controller-gen) +endif + +kustomize: +ifeq (, $(shell which kustomize)) + @{ \ + set -e ;\ + KUSTOMIZE_GEN_TMP_DIR=$$(mktemp -d) ;\ + cd $$KUSTOMIZE_GEN_TMP_DIR ;\ + go mod init tmp ;\ + go get sigs.k8s.io/kustomize/kustomize/v3@v3.5.4 ;\ + rm -rf $$KUSTOMIZE_GEN_TMP_DIR ;\ + } +KUSTOMIZE=$(GOBIN)/kustomize +else +KUSTOMIZE=$(shell which kustomize) +endif # In CircleCI, the linux binary will be attached from a previous step at pkg/bin/linux_amd64/. This make target # should only run in CI and not locally. diff --git a/commands.go b/commands.go index cd0f825837..c01d9cc138 100644 --- a/commands.go +++ b/commands.go @@ -4,6 +4,7 @@ import ( "os" cmdACLInit "github.com/hashicorp/consul-k8s/subcommand/acl-init" + cmdController "github.com/hashicorp/consul-k8s/subcommand/controller" cmdCreateFederationSecret "github.com/hashicorp/consul-k8s/subcommand/create-federation-secret" cmdDeleteCompletedJob "github.com/hashicorp/consul-k8s/subcommand/delete-completed-job" cmdGetConsulClientCA "github.com/hashicorp/consul-k8s/subcommand/get-consul-client-ca" @@ -63,6 +64,10 @@ func init() { "create-federation-secret": func() (cli.Command, error) { return &cmdCreateFederationSecret.Command{UI: ui}, nil }, + + "controller": func() (cli.Command, error) { + return &cmdController.Command{}, nil + }, } } diff --git a/controllers/servicedefaults_controller_test.go b/controllers/servicedefaults_controller_test.go index f6a560558a..305e1f7f04 100644 --- a/controllers/servicedefaults_controller_test.go +++ b/controllers/servicedefaults_controller_test.go @@ -35,7 +35,6 @@ import ( ) func TestServiceDefaultsController_createsConfigEntry(t *testing.T) { - t.SkipNow() req := require.New(t) svcDefaults := &v1alpha1.ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -143,7 +142,6 @@ func TestServiceDefaultsController_addsFinalizerOnCreate(t *testing.T) { } func TestServiceDefaultsController_updatesConfigEntry(t *testing.T) { - t.SkipNow() req := require.New(t) svcDefaults := &v1alpha1.ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -315,7 +313,7 @@ func TestServiceDefaultsController_errorUpdatesSyncStatus(t *testing.T) { Name: svcDefaults.Name, }, }) - req.EqualError(err, "Get \"http://incorrect-address/v1/config/service-defaults/foo\": dial tcp: lookup incorrect-address: no such host") + req.EqualError(err, "Get \"http://incorrect-address/v1/config/service-defaults/foo\": dial tcp: lookup incorrect-address on 127.0.0.11:53: no such host") req.False(resp.Requeue) // Check that the status is "synced=false". @@ -327,5 +325,5 @@ func TestServiceDefaultsController_errorUpdatesSyncStatus(t *testing.T) { conditionSynced := svcDefaults.Status.GetCondition(v1alpha1.ConditionSynced) req.True(conditionSynced.IsFalse()) req.Equal("ConsulAgentError", conditionSynced.Reason) - req.Equal("Get \"http://incorrect-address/v1/config/service-defaults/foo\": dial tcp: lookup incorrect-address: no such host", conditionSynced.Message) + req.Equal("Get \"http://incorrect-address/v1/config/service-defaults/foo\": dial tcp: lookup incorrect-address on 127.0.0.11:53: no such host", conditionSynced.Message) } diff --git a/go.mod b/go.mod index 6f4032f8eb..216d39470c 100644 --- a/go.mod +++ b/go.mod @@ -1,32 +1,43 @@ module github.com/hashicorp/consul-k8s require ( - github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect + github.com/armon/go-metrics v0.3.4 // indirect github.com/cenkalti/backoff v2.1.1+incompatible github.com/deckarep/golang-set v1.7.1 github.com/digitalocean/godo v1.10.0 // indirect github.com/go-logr/logr v0.1.0 + github.com/google/go-querystring v1.0.0 // indirect github.com/hashicorp/consul/api v1.6.0 github.com/hashicorp/consul/sdk v0.6.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v0.12.0 - github.com/hashicorp/go-immutable-radix v1.1.0 // indirect + github.com/hashicorp/go-immutable-radix v1.2.0 // indirect github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/kr/text v0.1.0 github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/go-testing-interface v1.14.0 // indirect + github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/onsi/ginkgo v1.12.1 github.com/onsi/gomega v1.10.1 github.com/radovskyb/watcher v1.0.2 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.5.1 + go.opencensus.io v0.22.0 // indirect golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 // indirect golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect + golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect + golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/api v0.9.0 // indirect + google.golang.org/appengine v1.6.0 // indirect k8s.io/api v0.18.2 k8s.io/apimachinery v0.18.2 k8s.io/client-go v0.18.2 diff --git a/go.sum b/go.sum index d6e2ec13ee..8d16fba9c5 100644 --- a/go.sum +++ b/go.sum @@ -36,9 +36,10 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4= -github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -48,14 +49,16 @@ github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KM github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-metrics v0.3.4 h1:Xqf+7f2Vhl9tsqDYmXhnXInUdcrtgpRNpIA15/uldSc= +github.com/armon/go-metrics v0.3.4/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= @@ -68,20 +71,30 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -89,6 +102,7 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -100,6 +114,7 @@ github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72 github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/digitalocean/godo v1.7.5/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1xXY= github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= @@ -118,6 +133,7 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -134,7 +150,9 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= @@ -219,6 +237,8 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= @@ -229,6 +249,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= @@ -240,8 +262,10 @@ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/consul/api v1.6.0 h1:SZB2hQW8AcTOpfDmiVblQbijxzsRuiyy0JpHfabvHio= github.com/hashicorp/consul/api v1.6.0/go.mod h1:1NSuaUUkFaJzMasbfq/11wKYWSR67Xn6r2DXKhuDNFg= @@ -258,8 +282,8 @@ github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tk github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc= -github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.2.0 h1:l6UW37iCXwZkZoAbEYnptSHVE/cQ5bOTPYG5W3vf9+8= +github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -278,9 +302,14 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -298,6 +327,8 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= @@ -305,10 +336,14 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= +github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= +github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -316,6 +351,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= @@ -324,6 +361,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -348,6 +386,7 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= @@ -362,10 +401,14 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= +github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -380,7 +423,9 @@ github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= @@ -413,33 +458,46 @@ github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXq github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0 h1:YVIb/fVcOTMSqtqZWSKnHpSLBxu8DKgxq8z6RuBZwqI= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/radovskyb/watcher v1.0.2 h1:9L5TsZUbo1nKhQEQPtICVc+x9UZQ6VPdBepLHyGw/bQ= github.com/radovskyb/watcher v1.0.2/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= +github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= @@ -449,7 +507,9 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -461,6 +521,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -472,11 +533,16 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= @@ -484,6 +550,7 @@ github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -491,6 +558,8 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -515,6 +584,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -522,13 +592,15 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -568,6 +640,9 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -578,12 +653,15 @@ golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -592,8 +670,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -603,6 +681,7 @@ golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -617,19 +696,27 @@ gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0 gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw= +google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= @@ -665,6 +752,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= @@ -672,6 +760,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go new file mode 100644 index 0000000000..4b9619aa30 --- /dev/null +++ b/subcommand/controller/command.go @@ -0,0 +1,263 @@ +package controller + +import ( + "flag" + "io/ioutil" + "os" + "strings" + "sync" + + "github.com/hashicorp/consul-k8s/api/v1alpha1" + "github.com/hashicorp/consul-k8s/controllers" + "github.com/hashicorp/consul-k8s/subcommand/flags" + "github.com/hashicorp/consul/api" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/kubernetes" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +type Command struct { + flags *flag.FlagSet + k8s *flags.K8SFlags + flagSecretName string + flagInitType string + flagNamespace string + flagACLDir string + flagTokenSinkFile string + + k8sClient kubernetes.Interface + + once sync.Once + help string +} + +func (c Command) Help() string { + panic("implement me") +} + +func (c Command) Synopsis() string { + panic("implement me") +} + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(v1alpha1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + +func (c Command) Run(args []string) int { + var metricsAddr string + var enableLeaderElection bool + c.flags = flag.NewFlagSet("", flag.ContinueOnError) + c.flags.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + c.flags.BoolVar(&enableLeaderElection, "enable-leader-election", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + httpFlags := HTTPFlags{} + Merge(c.flags, httpFlags.Flags()) + if err := c.flags.Parse(nil); err != nil { + setupLog.Error(err, "parsing flags") + return 1 + } + + ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + Port: 9443, + LeaderElection: enableLeaderElection, + LeaderElectionID: "65a0bb41.my.domain", + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + return 1 + } + + consulClient, err := httpFlags.APIClient() + if err != nil { + setupLog.Error(err, "connecting to Consul agent") + return 1 + } + + if err = (&controllers.ServiceDefaultsReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("ServiceDefaults"), + Scheme: mgr.GetScheme(), + ConsulClient: consulClient, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ServiceDefaults") + return 1 + } + // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. + // Instead we should implement our own validating webhooks so we can pass in the clients. + v1alpha1.ConsulClient = consulClient + v1alpha1.KubeClient = mgr.GetClient() + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err = (&v1alpha1.ServiceDefaults{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ServiceDefaults") + os.Exit(1) + } + } + // +kubebuilder:scaffold:builder + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + return 1 + } + return 0 +} + +// Taken from https://github.com/hashicorp/consul/blob/b5b9c8d953cd3c79c6b795946839f4cf5012f507/command/flags/http.go +// with flags we don't use removed. This was done so we don't depend on internal +// Consul implementation. + +// HTTPFlags are flags used to configure communication with a Consul agent. +type HTTPFlags struct { + address StringValue + token StringValue + tokenFile StringValue + caFile StringValue + caPath StringValue + certFile StringValue + keyFile StringValue + tlsServerName StringValue +} + +func (f *HTTPFlags) Flags() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.Var(&f.address, "http-addr", + "The `address` and port of the Consul HTTP agent. The value can be an IP "+ + "address or DNS address, but it must also include the port. This can "+ + "also be specified via the CONSUL_HTTP_ADDR environment variable. The "+ + "default value is http://127.0.0.1:8500. The scheme can also be set to "+ + "HTTPS by setting the environment variable CONSUL_HTTP_SSL=true.") + fs.Var(&f.token, "token", + "ACL token to use in the request. This can also be specified via the "+ + "CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+ + "default to the token of the Consul agent at the HTTP address.") + fs.Var(&f.tokenFile, "token-file", + "File containing the ACL token to use in the request instead of one specified "+ + "via the -token argument or CONSUL_HTTP_TOKEN environment variable. "+ + "This can also be specified via the CONSUL_HTTP_TOKEN_FILE environment variable.") + fs.Var(&f.caFile, "ca-file", + "Path to a CA file to use for TLS when communicating with Consul. This "+ + "can also be specified via the CONSUL_CACERT environment variable.") + fs.Var(&f.caPath, "ca-path", + "Path to a directory of CA certificates to use for TLS when communicating "+ + "with Consul. This can also be specified via the CONSUL_CAPATH environment variable.") + fs.Var(&f.certFile, "client-cert", + "Path to a client cert file to use for TLS when 'verify_incoming' is enabled. This "+ + "can also be specified via the CONSUL_CLIENT_CERT environment variable.") + fs.Var(&f.keyFile, "client-key", + "Path to a client key file to use for TLS when 'verify_incoming' is enabled. This "+ + "can also be specified via the CONSUL_CLIENT_KEY environment variable.") + fs.Var(&f.tlsServerName, "tls-server-name", + "The server name to use as the SNI host when connecting via TLS. This "+ + "can also be specified via the CONSUL_TLS_SERVER_NAME environment variable.") + return fs +} + +func (f *HTTPFlags) Addr() string { + return f.address.String() +} + +func (f *HTTPFlags) Token() string { + return f.token.String() +} + +func (f *HTTPFlags) SetToken(v string) error { + return f.token.Set(v) +} + +func (f *HTTPFlags) TokenFile() string { + return f.tokenFile.String() +} + +func (f *HTTPFlags) SetTokenFile(v string) error { + return f.tokenFile.Set(v) +} + +func (f *HTTPFlags) ReadTokenFile() (string, error) { + tokenFile := f.tokenFile.String() + if tokenFile == "" { + return "", nil + } + + data, err := ioutil.ReadFile(tokenFile) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(data)), nil +} + +func (f *HTTPFlags) APIClient() (*api.Client, error) { + c := api.DefaultConfig() + + f.MergeOntoConfig(c) + + return api.NewClient(c) +} + +func (f *HTTPFlags) MergeOntoConfig(c *api.Config) { + f.address.Merge(&c.Address) + f.token.Merge(&c.Token) + f.tokenFile.Merge(&c.TokenFile) + f.caFile.Merge(&c.TLSConfig.CAFile) + f.caPath.Merge(&c.TLSConfig.CAPath) + f.certFile.Merge(&c.TLSConfig.CertFile) + f.keyFile.Merge(&c.TLSConfig.KeyFile) + f.tlsServerName.Merge(&c.TLSConfig.Address) +} + +func Merge(dst, src *flag.FlagSet) { + if dst == nil { + panic("dst cannot be nil") + } + if src == nil { + return + } + src.VisitAll(func(f *flag.Flag) { + dst.Var(f.Value, f.Name, f.Usage) + }) +} + +// StringValue provides a flag value that's aware if it has been set. +type StringValue struct { + v *string +} + +// Merge will overlay this value if it has been set. +func (s *StringValue) Merge(onto *string) { + if s.v != nil { + *onto = *(s.v) + } +} + +// Set implements the flag.Value interface. +func (s *StringValue) Set(v string) error { + if s.v == nil { + s.v = new(string) + } + *(s.v) = v + return nil +} + +// String implements the flag.Value interface. +func (s *StringValue) String() string { + var current string + if s.v != nil { + current = *(s.v) + } + return current +} From 3bcc12ddd2c4e0a79e96bc9a2e662fab6b00c787 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 14 Aug 2020 14:40:53 -0400 Subject: [PATCH 03/20] Update gitignore to ignore .idea files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 14f1aa298b..80cbe9bc42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .DS_Store /bin /pkg + +.idea/ From d62d0cd4af7678314f4edfa5a306c8305370a0f5 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 14 Aug 2020 14:57:33 -0400 Subject: [PATCH 04/20] Use flags from the Command struct and update behavior for help and synopsis --- controller.go | 482 +++++++++++++++---------------- subcommand/controller/command.go | 192 ++---------- 2 files changed, 271 insertions(+), 403 deletions(-) diff --git a/controller.go b/controller.go index f517ae010c..437c1043e9 100644 --- a/controller.go +++ b/controller.go @@ -16,246 +16,246 @@ limitations under the License. package main -import ( - "flag" - "io/ioutil" - "os" - "strings" - - "github.com/hashicorp/consul/api" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - "github.com/hashicorp/consul-k8s/api/v1alpha1" - "github.com/hashicorp/consul-k8s/controllers" - // +kubebuilder:scaffold:imports -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(v1alpha1.AddToScheme(scheme)) - // +kubebuilder:scaffold:scheme -} - +//import ( +// "flag" +// "io/ioutil" +// "os" +// "strings" // -//func main() { -// os.Exit(realMain()) +// "github.com/hashicorp/consul/api" +// "k8s.io/apimachinery/pkg/runtime" +// utilruntime "k8s.io/apimachinery/pkg/util/runtime" +// clientgoscheme "k8s.io/client-go/kubernetes/scheme" +// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" +// ctrl "sigs.k8s.io/controller-runtime" +// "sigs.k8s.io/controller-runtime/pkg/log/zap" +// +// "github.com/hashicorp/consul-k8s/api/v1alpha1" +// "github.com/hashicorp/consul-k8s/controllers" +// // +kubebuilder:scaffold:imports +//) +// +//var ( +// scheme = runtime.NewScheme() +// setupLog = ctrl.Log.WithName("setup") +//) +// +//func init() { +// utilruntime.Must(clientgoscheme.AddToScheme(scheme)) +// utilruntime.Must(v1alpha1.AddToScheme(scheme)) +// // +kubebuilder:scaffold:scheme +//} +// +//// +////func main() { +//// os.Exit(realMain()) +////} +// +//func realMain() int { +// var metricsAddr string +// var enableLeaderElection bool +// flags := flag.NewFlagSet("", flag.ContinueOnError) +// flags.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") +// flags.BoolVar(&enableLeaderElection, "enable-leader-election", false, +// "Enable leader election for controller manager. "+ +// "Enabling this will ensure there is only one active controller manager.") +// httpFlags := HTTPFlags{} +// Merge(flags, httpFlags.Flags()) +// if err := flags.Parse(nil); err != nil { +// setupLog.Error(err, "parsing flags") +// return 1 +// } +// +// ctrl.SetLogger(zap.New(zap.UseDevMode(true))) +// +// mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ +// Scheme: scheme, +// MetricsBindAddress: metricsAddr, +// Port: 9443, +// LeaderElection: enableLeaderElection, +// LeaderElectionID: "65a0bb41.my.domain", +// }) +// if err != nil { +// setupLog.Error(err, "unable to start manager") +// return 1 +// } +// +// consulClient, err := httpFlags.APIClient() +// if err != nil { +// setupLog.Error(err, "connecting to Consul agent") +// return 1 +// } +// +// if err = (&controllers.ServiceDefaultsReconciler{ +// Client: mgr.GetClient(), +// Log: ctrl.Log.WithName("controllers").WithName("ServiceDefaults"), +// Scheme: mgr.GetScheme(), +// ConsulClient: consulClient, +// }).SetupWithManager(mgr); err != nil { +// setupLog.Error(err, "unable to create controller", "controller", "ServiceDefaults") +// return 1 +// } +// // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. +// // Instead we should implement our own validating webhooks so we can pass in the clients. +// v1alpha1.ConsulClient = consulClient +// v1alpha1.KubeClient = mgr.GetClient() +// if os.Getenv("ENABLE_WEBHOOKS") != "false" { +// if err = (&v1alpha1.ServiceDefaults{}).SetupWebhookWithManager(mgr); err != nil { +// setupLog.Error(err, "unable to create webhook", "webhook", "ServiceDefaults") +// os.Exit(1) +// } +// } +// // +kubebuilder:scaffold:builder +// +// setupLog.Info("starting manager") +// if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { +// setupLog.Error(err, "problem running manager") +// return 1 +// } +// return 0 +//} +// +//// Taken from https://github.com/hashicorp/consul/blob/b5b9c8d953cd3c79c6b795946839f4cf5012f507/command/flags/http.go +//// with flags we don't use removed. This was done so we don't depend on internal +//// Consul implementation. +// +//// HTTPFlags are flags used to configure communication with a Consul agent. +//type HTTPFlags struct { +// address StringValue +// token StringValue +// tokenFile StringValue +// caFile StringValue +// caPath StringValue +// certFile StringValue +// keyFile StringValue +// tlsServerName StringValue +//} +// +//func (f *HTTPFlags) Flags() *flag.FlagSet { +// fs := flag.NewFlagSet("", flag.ContinueOnError) +// fs.Var(&f.address, "http-addr", +// "The `address` and port of the Consul HTTP agent. The value can be an IP "+ +// "address or DNS address, but it must also include the port. This can "+ +// "also be specified via the CONSUL_HTTP_ADDR environment variable. The "+ +// "default value is http://127.0.0.1:8500. The scheme can also be set to "+ +// "HTTPS by setting the environment variable CONSUL_HTTP_SSL=true.") +// fs.Var(&f.token, "token", +// "ACL token to use in the request. This can also be specified via the "+ +// "CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+ +// "default to the token of the Consul agent at the HTTP address.") +// fs.Var(&f.tokenFile, "token-file", +// "File containing the ACL token to use in the request instead of one specified "+ +// "via the -token argument or CONSUL_HTTP_TOKEN environment variable. "+ +// "This can also be specified via the CONSUL_HTTP_TOKEN_FILE environment variable.") +// fs.Var(&f.caFile, "ca-file", +// "Path to a CA file to use for TLS when communicating with Consul. This "+ +// "can also be specified via the CONSUL_CACERT environment variable.") +// fs.Var(&f.caPath, "ca-path", +// "Path to a directory of CA certificates to use for TLS when communicating "+ +// "with Consul. This can also be specified via the CONSUL_CAPATH environment variable.") +// fs.Var(&f.certFile, "client-cert", +// "Path to a client cert file to use for TLS when 'verify_incoming' is enabled. This "+ +// "can also be specified via the CONSUL_CLIENT_CERT environment variable.") +// fs.Var(&f.keyFile, "client-key", +// "Path to a client key file to use for TLS when 'verify_incoming' is enabled. This "+ +// "can also be specified via the CONSUL_CLIENT_KEY environment variable.") +// fs.Var(&f.tlsServerName, "tls-server-name", +// "The server name to use as the SNI host when connecting via TLS. This "+ +// "can also be specified via the CONSUL_TLS_SERVER_NAME environment variable.") +// return fs +//} +// +//func (f *HTTPFlags) Addr() string { +// return f.address.String() +//} +// +//func (f *HTTPFlags) Token() string { +// return f.token.String() +//} +// +//func (f *HTTPFlags) SetToken(v string) error { +// return f.token.Set(v) +//} +// +//func (f *HTTPFlags) TokenFile() string { +// return f.tokenFile.String() +//} +// +//func (f *HTTPFlags) SetTokenFile(v string) error { +// return f.tokenFile.Set(v) +//} +// +//func (f *HTTPFlags) ReadTokenFile() (string, error) { +// tokenFile := f.tokenFile.String() +// if tokenFile == "" { +// return "", nil +// } +// +// data, err := ioutil.ReadFile(tokenFile) +// if err != nil { +// return "", err +// } +// +// return strings.TrimSpace(string(data)), nil +//} +// +//func (f *HTTPFlags) APIClient() (*api.Client, error) { +// c := api.DefaultConfig() +// +// f.MergeOntoConfig(c) +// +// return api.NewClient(c) +//} +// +//func (f *HTTPFlags) MergeOntoConfig(c *api.Config) { +// f.address.Merge(&c.Address) +// f.token.Merge(&c.Token) +// f.tokenFile.Merge(&c.TokenFile) +// f.caFile.Merge(&c.TLSConfig.CAFile) +// f.caPath.Merge(&c.TLSConfig.CAPath) +// f.certFile.Merge(&c.TLSConfig.CertFile) +// f.keyFile.Merge(&c.TLSConfig.KeyFile) +// f.tlsServerName.Merge(&c.TLSConfig.Address) +//} +// +//func Merge(dst, src *flag.FlagSet) { +// if dst == nil { +// panic("dst cannot be nil") +// } +// if src == nil { +// return +// } +// src.VisitAll(func(f *flag.Flag) { +// dst.Var(f.Value, f.Name, f.Usage) +// }) +//} +// +//// StringValue provides a flag value that's aware if it has been set. +//type StringValue struct { +// v *string +//} +// +//// Merge will overlay this value if it has been set. +//func (s *StringValue) Merge(onto *string) { +// if s.v != nil { +// *onto = *(s.v) +// } +//} +// +//// Set implements the flag.Value interface. +//func (s *StringValue) Set(v string) error { +// if s.v == nil { +// s.v = new(string) +// } +// *(s.v) = v +// return nil +//} +// +//// String implements the flag.Value interface. +//func (s *StringValue) String() string { +// var current string +// if s.v != nil { +// current = *(s.v) +// } +// return current //} - -func realMain() int { - var metricsAddr string - var enableLeaderElection bool - flags := flag.NewFlagSet("", flag.ContinueOnError) - flags.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - flags.BoolVar(&enableLeaderElection, "enable-leader-election", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") - httpFlags := HTTPFlags{} - Merge(flags, httpFlags.Flags()) - if err := flags.Parse(nil); err != nil { - setupLog.Error(err, "parsing flags") - return 1 - } - - ctrl.SetLogger(zap.New(zap.UseDevMode(true))) - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - LeaderElection: enableLeaderElection, - LeaderElectionID: "65a0bb41.my.domain", - }) - if err != nil { - setupLog.Error(err, "unable to start manager") - return 1 - } - - consulClient, err := httpFlags.APIClient() - if err != nil { - setupLog.Error(err, "connecting to Consul agent") - return 1 - } - - if err = (&controllers.ServiceDefaultsReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("ServiceDefaults"), - Scheme: mgr.GetScheme(), - ConsulClient: consulClient, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "ServiceDefaults") - return 1 - } - // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. - // Instead we should implement our own validating webhooks so we can pass in the clients. - v1alpha1.ConsulClient = consulClient - v1alpha1.KubeClient = mgr.GetClient() - if os.Getenv("ENABLE_WEBHOOKS") != "false" { - if err = (&v1alpha1.ServiceDefaults{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ServiceDefaults") - os.Exit(1) - } - } - // +kubebuilder:scaffold:builder - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - return 1 - } - return 0 -} - -// Taken from https://github.com/hashicorp/consul/blob/b5b9c8d953cd3c79c6b795946839f4cf5012f507/command/flags/http.go -// with flags we don't use removed. This was done so we don't depend on internal -// Consul implementation. - -// HTTPFlags are flags used to configure communication with a Consul agent. -type HTTPFlags struct { - address StringValue - token StringValue - tokenFile StringValue - caFile StringValue - caPath StringValue - certFile StringValue - keyFile StringValue - tlsServerName StringValue -} - -func (f *HTTPFlags) Flags() *flag.FlagSet { - fs := flag.NewFlagSet("", flag.ContinueOnError) - fs.Var(&f.address, "http-addr", - "The `address` and port of the Consul HTTP agent. The value can be an IP "+ - "address or DNS address, but it must also include the port. This can "+ - "also be specified via the CONSUL_HTTP_ADDR environment variable. The "+ - "default value is http://127.0.0.1:8500. The scheme can also be set to "+ - "HTTPS by setting the environment variable CONSUL_HTTP_SSL=true.") - fs.Var(&f.token, "token", - "ACL token to use in the request. This can also be specified via the "+ - "CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+ - "default to the token of the Consul agent at the HTTP address.") - fs.Var(&f.tokenFile, "token-file", - "File containing the ACL token to use in the request instead of one specified "+ - "via the -token argument or CONSUL_HTTP_TOKEN environment variable. "+ - "This can also be specified via the CONSUL_HTTP_TOKEN_FILE environment variable.") - fs.Var(&f.caFile, "ca-file", - "Path to a CA file to use for TLS when communicating with Consul. This "+ - "can also be specified via the CONSUL_CACERT environment variable.") - fs.Var(&f.caPath, "ca-path", - "Path to a directory of CA certificates to use for TLS when communicating "+ - "with Consul. This can also be specified via the CONSUL_CAPATH environment variable.") - fs.Var(&f.certFile, "client-cert", - "Path to a client cert file to use for TLS when 'verify_incoming' is enabled. This "+ - "can also be specified via the CONSUL_CLIENT_CERT environment variable.") - fs.Var(&f.keyFile, "client-key", - "Path to a client key file to use for TLS when 'verify_incoming' is enabled. This "+ - "can also be specified via the CONSUL_CLIENT_KEY environment variable.") - fs.Var(&f.tlsServerName, "tls-server-name", - "The server name to use as the SNI host when connecting via TLS. This "+ - "can also be specified via the CONSUL_TLS_SERVER_NAME environment variable.") - return fs -} - -func (f *HTTPFlags) Addr() string { - return f.address.String() -} - -func (f *HTTPFlags) Token() string { - return f.token.String() -} - -func (f *HTTPFlags) SetToken(v string) error { - return f.token.Set(v) -} - -func (f *HTTPFlags) TokenFile() string { - return f.tokenFile.String() -} - -func (f *HTTPFlags) SetTokenFile(v string) error { - return f.tokenFile.Set(v) -} - -func (f *HTTPFlags) ReadTokenFile() (string, error) { - tokenFile := f.tokenFile.String() - if tokenFile == "" { - return "", nil - } - - data, err := ioutil.ReadFile(tokenFile) - if err != nil { - return "", err - } - - return strings.TrimSpace(string(data)), nil -} - -func (f *HTTPFlags) APIClient() (*api.Client, error) { - c := api.DefaultConfig() - - f.MergeOntoConfig(c) - - return api.NewClient(c) -} - -func (f *HTTPFlags) MergeOntoConfig(c *api.Config) { - f.address.Merge(&c.Address) - f.token.Merge(&c.Token) - f.tokenFile.Merge(&c.TokenFile) - f.caFile.Merge(&c.TLSConfig.CAFile) - f.caPath.Merge(&c.TLSConfig.CAPath) - f.certFile.Merge(&c.TLSConfig.CertFile) - f.keyFile.Merge(&c.TLSConfig.KeyFile) - f.tlsServerName.Merge(&c.TLSConfig.Address) -} - -func Merge(dst, src *flag.FlagSet) { - if dst == nil { - panic("dst cannot be nil") - } - if src == nil { - return - } - src.VisitAll(func(f *flag.Flag) { - dst.Var(f.Value, f.Name, f.Usage) - }) -} - -// StringValue provides a flag value that's aware if it has been set. -type StringValue struct { - v *string -} - -// Merge will overlay this value if it has been set. -func (s *StringValue) Merge(onto *string) { - if s.v != nil { - *onto = *(s.v) - } -} - -// Set implements the flag.Value interface. -func (s *StringValue) Set(v string) error { - if s.v == nil { - s.v = new(string) - } - *(s.v) = v - return nil -} - -// String implements the flag.Value interface. -func (s *StringValue) String() string { - var current string - if s.v != nil { - current = *(s.v) - } - return current -} diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 4b9619aa30..8396d2af3e 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -2,15 +2,12 @@ package controller import ( "flag" - "io/ioutil" "os" - "strings" "sync" "github.com/hashicorp/consul-k8s/api/v1alpha1" "github.com/hashicorp/consul-k8s/controllers" "github.com/hashicorp/consul-k8s/subcommand/flags" - "github.com/hashicorp/consul/api" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes" @@ -20,13 +17,16 @@ import ( ) type Command struct { - flags *flag.FlagSet - k8s *flags.K8SFlags - flagSecretName string - flagInitType string - flagNamespace string - flagACLDir string - flagTokenSinkFile string + flags *flag.FlagSet + k8s *flags.K8SFlags + httpFlags *flags.HTTPFlags + metricsAddr string + enableLeaderElection bool + flagSecretName string + flagInitType string + flagNamespace string + flagACLDir string + flagTokenSinkFile string k8sClient kubernetes.Interface @@ -34,12 +34,13 @@ type Command struct { help string } -func (c Command) Help() string { - panic("implement me") +func (c *Command) Help() string { + c.once.Do(c.init) + return c.help } -func (c Command) Synopsis() string { - panic("implement me") +func (c *Command) Synopsis() string { + return help } var ( @@ -53,7 +54,7 @@ func init() { // +kubebuilder:scaffold:scheme } -func (c Command) Run(args []string) int { +func (c *Command) init() { var metricsAddr string var enableLeaderElection bool c.flags = flag.NewFlagSet("", flag.ContinueOnError) @@ -61,8 +62,13 @@ func (c Command) Run(args []string) int { c.flags.BoolVar(&enableLeaderElection, "enable-leader-election", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - httpFlags := HTTPFlags{} - Merge(c.flags, httpFlags.Flags()) + flags.Merge(c.flags, c.httpFlags.Flags()) + c.help = flags.Usage(help, c.flags) +} + +func (c *Command) Run(args []string) int { + c.once.Do(c.init) + if err := c.flags.Parse(nil); err != nil { setupLog.Error(err, "parsing flags") return 1 @@ -72,9 +78,9 @@ func (c Command) Run(args []string) int { mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, - MetricsBindAddress: metricsAddr, + MetricsBindAddress: c.metricsAddr, Port: 9443, - LeaderElection: enableLeaderElection, + LeaderElection: c.enableLeaderElection, LeaderElectionID: "65a0bb41.my.domain", }) if err != nil { @@ -82,7 +88,7 @@ func (c Command) Run(args []string) int { return 1 } - consulClient, err := httpFlags.APIClient() + consulClient, err := c.httpFlags.APIClient() if err != nil { setupLog.Error(err, "connecting to Consul agent") return 1 @@ -117,147 +123,9 @@ func (c Command) Run(args []string) int { return 0 } -// Taken from https://github.com/hashicorp/consul/blob/b5b9c8d953cd3c79c6b795946839f4cf5012f507/command/flags/http.go -// with flags we don't use removed. This was done so we don't depend on internal -// Consul implementation. - -// HTTPFlags are flags used to configure communication with a Consul agent. -type HTTPFlags struct { - address StringValue - token StringValue - tokenFile StringValue - caFile StringValue - caPath StringValue - certFile StringValue - keyFile StringValue - tlsServerName StringValue -} - -func (f *HTTPFlags) Flags() *flag.FlagSet { - fs := flag.NewFlagSet("", flag.ContinueOnError) - fs.Var(&f.address, "http-addr", - "The `address` and port of the Consul HTTP agent. The value can be an IP "+ - "address or DNS address, but it must also include the port. This can "+ - "also be specified via the CONSUL_HTTP_ADDR environment variable. The "+ - "default value is http://127.0.0.1:8500. The scheme can also be set to "+ - "HTTPS by setting the environment variable CONSUL_HTTP_SSL=true.") - fs.Var(&f.token, "token", - "ACL token to use in the request. This can also be specified via the "+ - "CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+ - "default to the token of the Consul agent at the HTTP address.") - fs.Var(&f.tokenFile, "token-file", - "File containing the ACL token to use in the request instead of one specified "+ - "via the -token argument or CONSUL_HTTP_TOKEN environment variable. "+ - "This can also be specified via the CONSUL_HTTP_TOKEN_FILE environment variable.") - fs.Var(&f.caFile, "ca-file", - "Path to a CA file to use for TLS when communicating with Consul. This "+ - "can also be specified via the CONSUL_CACERT environment variable.") - fs.Var(&f.caPath, "ca-path", - "Path to a directory of CA certificates to use for TLS when communicating "+ - "with Consul. This can also be specified via the CONSUL_CAPATH environment variable.") - fs.Var(&f.certFile, "client-cert", - "Path to a client cert file to use for TLS when 'verify_incoming' is enabled. This "+ - "can also be specified via the CONSUL_CLIENT_CERT environment variable.") - fs.Var(&f.keyFile, "client-key", - "Path to a client key file to use for TLS when 'verify_incoming' is enabled. This "+ - "can also be specified via the CONSUL_CLIENT_KEY environment variable.") - fs.Var(&f.tlsServerName, "tls-server-name", - "The server name to use as the SNI host when connecting via TLS. This "+ - "can also be specified via the CONSUL_TLS_SERVER_NAME environment variable.") - return fs -} - -func (f *HTTPFlags) Addr() string { - return f.address.String() -} - -func (f *HTTPFlags) Token() string { - return f.token.String() -} - -func (f *HTTPFlags) SetToken(v string) error { - return f.token.Set(v) -} - -func (f *HTTPFlags) TokenFile() string { - return f.tokenFile.String() -} - -func (f *HTTPFlags) SetTokenFile(v string) error { - return f.tokenFile.Set(v) -} - -func (f *HTTPFlags) ReadTokenFile() (string, error) { - tokenFile := f.tokenFile.String() - if tokenFile == "" { - return "", nil - } - - data, err := ioutil.ReadFile(tokenFile) - if err != nil { - return "", err - } - - return strings.TrimSpace(string(data)), nil -} - -func (f *HTTPFlags) APIClient() (*api.Client, error) { - c := api.DefaultConfig() - - f.MergeOntoConfig(c) - - return api.NewClient(c) -} - -func (f *HTTPFlags) MergeOntoConfig(c *api.Config) { - f.address.Merge(&c.Address) - f.token.Merge(&c.Token) - f.tokenFile.Merge(&c.TokenFile) - f.caFile.Merge(&c.TLSConfig.CAFile) - f.caPath.Merge(&c.TLSConfig.CAPath) - f.certFile.Merge(&c.TLSConfig.CertFile) - f.keyFile.Merge(&c.TLSConfig.KeyFile) - f.tlsServerName.Merge(&c.TLSConfig.Address) -} - -func Merge(dst, src *flag.FlagSet) { - if dst == nil { - panic("dst cannot be nil") - } - if src == nil { - return - } - src.VisitAll(func(f *flag.Flag) { - dst.Var(f.Value, f.Name, f.Usage) - }) -} - -// StringValue provides a flag value that's aware if it has been set. -type StringValue struct { - v *string -} +const help = ` +Usage: consul-k8s controller [options] -// Merge will overlay this value if it has been set. -func (s *StringValue) Merge(onto *string) { - if s.v != nil { - *onto = *(s.v) - } -} + Starts the consul kubernetes controller -// Set implements the flag.Value interface. -func (s *StringValue) Set(v string) error { - if s.v == nil { - s.v = new(string) - } - *(s.v) = v - return nil -} - -// String implements the flag.Value interface. -func (s *StringValue) String() string { - var current string - if s.v != nil { - current = *(s.v) - } - return current -} +` From d97847ffff61b39bdade4f2cad5425ffb9692965 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 14 Aug 2020 16:22:19 -0400 Subject: [PATCH 05/20] Update Make commands, undo gitignore changes --- .gitignore | 2 - Makefile | 13 +- api/v1alpha1/zz_generated.deepcopy.go | 16 -- .../consul.hashicorp.com_servicedefaults.yaml | 7 +- controller.go | 261 ------------------ hack/boilerplate.go.txt | 15 - subcommand/controller/command.go | 3 - 7 files changed, 12 insertions(+), 305 deletions(-) delete mode 100644 controller.go diff --git a/.gitignore b/.gitignore index 80cbe9bc42..14f1aa298b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ .DS_Store /bin /pkg - -.idea/ diff --git a/Makefile b/Makefile index 5e94a3f764..1e706b00b5 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ export GIT_DESCRIBE export GOLDFLAGS export GOTAGS - +CRD_OPTIONS ?= "crd:trivialVersions=true" ################ # CI Variables # @@ -128,12 +128,19 @@ clean: $(CURDIR)/bin \ $(CURDIR)/pkg +# Run controller tests +ENVTEST_ASSETS_DIR=$(shell pwd)/testbin +ctrl-test: ctrl-generate ctrl-manifests + mkdir -p ${ENVTEST_ASSETS_DIR} + test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/master/hack/setup-envtest.sh + source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out + # Generate manifests e.g. CRD, RBAC etc. -manifests: controller-gen +ctrl-manifests: controller-gen $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases # Generate code -generate: controller-gen +ctrl-generate: controller-gen $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." # find or download controller-gen diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 8347a745a4..54a8d3aedf 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1,21 +1,5 @@ // +build !ignore_autogenerated -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT 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 controller-gen. DO NOT EDIT. package v1alpha1 diff --git a/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 4cbcf52ab6..4d745010e3 100644 --- a/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -127,14 +127,11 @@ spec: type: array type: object type: object - version: v1alpha2 + version: v1alpha1 versions: - - name: v1alpha2 - served: true - storage: true - name: v1alpha1 served: true - storage: false + storage: true status: acceptedNames: kind: "" diff --git a/controller.go b/controller.go deleted file mode 100644 index 437c1043e9..0000000000 --- a/controller.go +++ /dev/null @@ -1,261 +0,0 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -//import ( -// "flag" -// "io/ioutil" -// "os" -// "strings" -// -// "github.com/hashicorp/consul/api" -// "k8s.io/apimachinery/pkg/runtime" -// utilruntime "k8s.io/apimachinery/pkg/util/runtime" -// clientgoscheme "k8s.io/client-go/kubernetes/scheme" -// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" -// ctrl "sigs.k8s.io/controller-runtime" -// "sigs.k8s.io/controller-runtime/pkg/log/zap" -// -// "github.com/hashicorp/consul-k8s/api/v1alpha1" -// "github.com/hashicorp/consul-k8s/controllers" -// // +kubebuilder:scaffold:imports -//) -// -//var ( -// scheme = runtime.NewScheme() -// setupLog = ctrl.Log.WithName("setup") -//) -// -//func init() { -// utilruntime.Must(clientgoscheme.AddToScheme(scheme)) -// utilruntime.Must(v1alpha1.AddToScheme(scheme)) -// // +kubebuilder:scaffold:scheme -//} -// -//// -////func main() { -//// os.Exit(realMain()) -////} -// -//func realMain() int { -// var metricsAddr string -// var enableLeaderElection bool -// flags := flag.NewFlagSet("", flag.ContinueOnError) -// flags.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") -// flags.BoolVar(&enableLeaderElection, "enable-leader-election", false, -// "Enable leader election for controller manager. "+ -// "Enabling this will ensure there is only one active controller manager.") -// httpFlags := HTTPFlags{} -// Merge(flags, httpFlags.Flags()) -// if err := flags.Parse(nil); err != nil { -// setupLog.Error(err, "parsing flags") -// return 1 -// } -// -// ctrl.SetLogger(zap.New(zap.UseDevMode(true))) -// -// mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ -// Scheme: scheme, -// MetricsBindAddress: metricsAddr, -// Port: 9443, -// LeaderElection: enableLeaderElection, -// LeaderElectionID: "65a0bb41.my.domain", -// }) -// if err != nil { -// setupLog.Error(err, "unable to start manager") -// return 1 -// } -// -// consulClient, err := httpFlags.APIClient() -// if err != nil { -// setupLog.Error(err, "connecting to Consul agent") -// return 1 -// } -// -// if err = (&controllers.ServiceDefaultsReconciler{ -// Client: mgr.GetClient(), -// Log: ctrl.Log.WithName("controllers").WithName("ServiceDefaults"), -// Scheme: mgr.GetScheme(), -// ConsulClient: consulClient, -// }).SetupWithManager(mgr); err != nil { -// setupLog.Error(err, "unable to create controller", "controller", "ServiceDefaults") -// return 1 -// } -// // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. -// // Instead we should implement our own validating webhooks so we can pass in the clients. -// v1alpha1.ConsulClient = consulClient -// v1alpha1.KubeClient = mgr.GetClient() -// if os.Getenv("ENABLE_WEBHOOKS") != "false" { -// if err = (&v1alpha1.ServiceDefaults{}).SetupWebhookWithManager(mgr); err != nil { -// setupLog.Error(err, "unable to create webhook", "webhook", "ServiceDefaults") -// os.Exit(1) -// } -// } -// // +kubebuilder:scaffold:builder -// -// setupLog.Info("starting manager") -// if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { -// setupLog.Error(err, "problem running manager") -// return 1 -// } -// return 0 -//} -// -//// Taken from https://github.com/hashicorp/consul/blob/b5b9c8d953cd3c79c6b795946839f4cf5012f507/command/flags/http.go -//// with flags we don't use removed. This was done so we don't depend on internal -//// Consul implementation. -// -//// HTTPFlags are flags used to configure communication with a Consul agent. -//type HTTPFlags struct { -// address StringValue -// token StringValue -// tokenFile StringValue -// caFile StringValue -// caPath StringValue -// certFile StringValue -// keyFile StringValue -// tlsServerName StringValue -//} -// -//func (f *HTTPFlags) Flags() *flag.FlagSet { -// fs := flag.NewFlagSet("", flag.ContinueOnError) -// fs.Var(&f.address, "http-addr", -// "The `address` and port of the Consul HTTP agent. The value can be an IP "+ -// "address or DNS address, but it must also include the port. This can "+ -// "also be specified via the CONSUL_HTTP_ADDR environment variable. The "+ -// "default value is http://127.0.0.1:8500. The scheme can also be set to "+ -// "HTTPS by setting the environment variable CONSUL_HTTP_SSL=true.") -// fs.Var(&f.token, "token", -// "ACL token to use in the request. This can also be specified via the "+ -// "CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+ -// "default to the token of the Consul agent at the HTTP address.") -// fs.Var(&f.tokenFile, "token-file", -// "File containing the ACL token to use in the request instead of one specified "+ -// "via the -token argument or CONSUL_HTTP_TOKEN environment variable. "+ -// "This can also be specified via the CONSUL_HTTP_TOKEN_FILE environment variable.") -// fs.Var(&f.caFile, "ca-file", -// "Path to a CA file to use for TLS when communicating with Consul. This "+ -// "can also be specified via the CONSUL_CACERT environment variable.") -// fs.Var(&f.caPath, "ca-path", -// "Path to a directory of CA certificates to use for TLS when communicating "+ -// "with Consul. This can also be specified via the CONSUL_CAPATH environment variable.") -// fs.Var(&f.certFile, "client-cert", -// "Path to a client cert file to use for TLS when 'verify_incoming' is enabled. This "+ -// "can also be specified via the CONSUL_CLIENT_CERT environment variable.") -// fs.Var(&f.keyFile, "client-key", -// "Path to a client key file to use for TLS when 'verify_incoming' is enabled. This "+ -// "can also be specified via the CONSUL_CLIENT_KEY environment variable.") -// fs.Var(&f.tlsServerName, "tls-server-name", -// "The server name to use as the SNI host when connecting via TLS. This "+ -// "can also be specified via the CONSUL_TLS_SERVER_NAME environment variable.") -// return fs -//} -// -//func (f *HTTPFlags) Addr() string { -// return f.address.String() -//} -// -//func (f *HTTPFlags) Token() string { -// return f.token.String() -//} -// -//func (f *HTTPFlags) SetToken(v string) error { -// return f.token.Set(v) -//} -// -//func (f *HTTPFlags) TokenFile() string { -// return f.tokenFile.String() -//} -// -//func (f *HTTPFlags) SetTokenFile(v string) error { -// return f.tokenFile.Set(v) -//} -// -//func (f *HTTPFlags) ReadTokenFile() (string, error) { -// tokenFile := f.tokenFile.String() -// if tokenFile == "" { -// return "", nil -// } -// -// data, err := ioutil.ReadFile(tokenFile) -// if err != nil { -// return "", err -// } -// -// return strings.TrimSpace(string(data)), nil -//} -// -//func (f *HTTPFlags) APIClient() (*api.Client, error) { -// c := api.DefaultConfig() -// -// f.MergeOntoConfig(c) -// -// return api.NewClient(c) -//} -// -//func (f *HTTPFlags) MergeOntoConfig(c *api.Config) { -// f.address.Merge(&c.Address) -// f.token.Merge(&c.Token) -// f.tokenFile.Merge(&c.TokenFile) -// f.caFile.Merge(&c.TLSConfig.CAFile) -// f.caPath.Merge(&c.TLSConfig.CAPath) -// f.certFile.Merge(&c.TLSConfig.CertFile) -// f.keyFile.Merge(&c.TLSConfig.KeyFile) -// f.tlsServerName.Merge(&c.TLSConfig.Address) -//} -// -//func Merge(dst, src *flag.FlagSet) { -// if dst == nil { -// panic("dst cannot be nil") -// } -// if src == nil { -// return -// } -// src.VisitAll(func(f *flag.Flag) { -// dst.Var(f.Value, f.Name, f.Usage) -// }) -//} -// -//// StringValue provides a flag value that's aware if it has been set. -//type StringValue struct { -// v *string -//} -// -//// Merge will overlay this value if it has been set. -//func (s *StringValue) Merge(onto *string) { -// if s.v != nil { -// *onto = *(s.v) -// } -//} -// -//// Set implements the flag.Value interface. -//func (s *StringValue) Set(v string) error { -// if s.v == nil { -// s.v = new(string) -// } -// *(s.v) = v -// return nil -//} -// -//// String implements the flag.Value interface. -//func (s *StringValue) String() string { -// var current string -// if s.v != nil { -// current = *(s.v) -// } -// return current -//} diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index 767efde981..e69de29bb2 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -1,15 +0,0 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ \ No newline at end of file diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 8396d2af3e..06f5db8804 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/consul-k8s/subcommand/flags" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/kubernetes" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -28,8 +27,6 @@ type Command struct { flagACLDir string flagTokenSinkFile string - k8sClient kubernetes.Interface - once sync.Once help string } From 1018548dc304174598b2c87a694437c0906141d7 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 14 Aug 2020 16:24:30 -0400 Subject: [PATCH 06/20] Remove Apache licence info from files(they were auto-generated) --- api/v1alpha1/groupversion_info.go | 16 ---------------- api/v1alpha1/servicedefaults_types.go | 16 ---------------- api/v1alpha1/servicedefaults_webhook.go | 16 ---------------- controllers/servicedefaults_controller.go | 16 ---------------- controllers/servicedefaults_controller_test.go | 16 ---------------- controllers/suite_test.go | 16 ---------------- 6 files changed, 96 deletions(-) diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index d375cb1154..200e710959 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -1,19 +1,3 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES 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 consul.hashicorp.com v1alpha1 API group // +kubebuilder:object:generate=true // +groupName=consul.hashicorp.com diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go index f80e8ea15d..b05a1b9306 100644 --- a/api/v1alpha1/servicedefaults_types.go +++ b/api/v1alpha1/servicedefaults_types.go @@ -1,19 +1,3 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES 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 ( diff --git a/api/v1alpha1/servicedefaults_webhook.go b/api/v1alpha1/servicedefaults_webhook.go index 465ae79a87..14873c7644 100644 --- a/api/v1alpha1/servicedefaults_webhook.go +++ b/api/v1alpha1/servicedefaults_webhook.go @@ -1,19 +1,3 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES 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 ( diff --git a/controllers/servicedefaults_controller.go b/controllers/servicedefaults_controller.go index 1c088a115d..d73139f099 100644 --- a/controllers/servicedefaults_controller.go +++ b/controllers/servicedefaults_controller.go @@ -1,19 +1,3 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - package controllers import ( diff --git a/controllers/servicedefaults_controller_test.go b/controllers/servicedefaults_controller_test.go index 305e1f7f04..99afcab346 100644 --- a/controllers/servicedefaults_controller_test.go +++ b/controllers/servicedefaults_controller_test.go @@ -1,19 +1,3 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - package controllers_test import ( diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 2c3980091d..d0fcae54a0 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -1,19 +1,3 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - package controllers import ( From 9cf07cf36c436460d1c0f9fbf9a2e41ed8a0793f Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Mon, 17 Aug 2020 13:04:09 -0400 Subject: [PATCH 07/20] Update Makefile and add PROJECT file to consul-k8s for operator-sdk autogen --- Makefile | 2 +- PROJECT | 6 ++++++ config/manager/manager.yaml | 3 ++- subcommand/controller/command.go | 6 ++---- 4 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 PROJECT diff --git a/Makefile b/Makefile index 1e706b00b5..33c27d65ee 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ else DEV_PUSH_ARG=--no-push endif -all: bin +all: bin ctrl-generate bin: @$(SHELL) $(CURDIR)/build-support/scripts/build-local.sh diff --git a/PROJECT b/PROJECT new file mode 100644 index 0000000000..931cdad64b --- /dev/null +++ b/PROJECT @@ -0,0 +1,6 @@ +domain: hashicorp.com +layout: go.kubebuilder.io/v2 +repo: github.com/hashicorp/consul-k8s +version: 3-alpha +plugins: + go.operator-sdk.io/v2-alpha: {} diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 5e01a00a12..b086533218 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -32,7 +32,8 @@ spec: - name: CONSUL_HTTP_ADDR value: http://$(HOST_IP):8500 command: - - /manager + - consul-k8s + - controller args: - --enable-leader-election image: controller:latest diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 06f5db8804..2941ed6f63 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -52,11 +52,9 @@ func init() { } func (c *Command) init() { - var metricsAddr string - var enableLeaderElection bool c.flags = flag.NewFlagSet("", flag.ContinueOnError) - c.flags.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - c.flags.BoolVar(&enableLeaderElection, "enable-leader-election", false, + c.flags.StringVar(&c.metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + c.flags.BoolVar(&c.enableLeaderElection, "enable-leader-election", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") flags.Merge(c.flags, c.httpFlags.Flags()) From 5a32358f103808bc473a68f0e847de01234d0614 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Mon, 24 Aug 2020 12:39:46 -0400 Subject: [PATCH 08/20] Update makefile with task to deploy controller - Controller now runs from consul-k8s image --- Makefile | 7 ++++++- config/manager/kustomization.yaml | 3 +-- subcommand/controller/command.go | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 33c27d65ee..12974a2984 100644 --- a/Makefile +++ b/Makefile @@ -133,7 +133,12 @@ ENVTEST_ASSETS_DIR=$(shell pwd)/testbin ctrl-test: ctrl-generate ctrl-manifests mkdir -p ${ENVTEST_ASSETS_DIR} test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/master/hack/setup-envtest.sh - source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out + source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... + +# Deploy controller in the configured Kubernetes cluster in ~/.kube/config +ctrl-deploy: ctrl-manifests kustomize + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | kubectl apply -f - # Generate manifests e.g. CRD, RBAC etc. ctrl-manifests: controller-gen diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5e2008f85e..8b71461da8 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,5 +5,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: lkysow/consul-k8s-controller - newTag: jul23 + newName: ashwinvenkatesh/consul-k8s diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 2941ed6f63..267a64670e 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -57,6 +57,7 @@ func (c *Command) init() { c.flags.BoolVar(&c.enableLeaderElection, "enable-leader-election", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") + c.httpFlags = &flags.HTTPFlags{} flags.Merge(c.flags, c.httpFlags.Flags()) c.help = flags.Usage(help, c.flags) } From 92f3a68c7a5ba7131225b100436f72a52d2514d0 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Mon, 24 Aug 2020 17:09:55 -0400 Subject: [PATCH 09/20] Update webhook to use external webhook pattern to inject consul client and kube client --- api/v1alpha1/servicedefaults_webhook.go | 80 +++++++------------- config/default/webhookcainjection_patch.yaml | 6 -- config/manager/kustomization.yaml | 1 + config/webhook/kustomizeconfig.yaml | 7 -- config/webhook/manifests.yaml | 26 ------- subcommand/controller/command.go | 11 ++- 6 files changed, 34 insertions(+), 97 deletions(-) diff --git a/api/v1alpha1/servicedefaults_webhook.go b/api/v1alpha1/servicedefaults_webhook.go index 14873c7644..0722954cea 100644 --- a/api/v1alpha1/servicedefaults_webhook.go +++ b/api/v1alpha1/servicedefaults_webhook.go @@ -3,74 +3,50 @@ package v1alpha1 import ( "context" "fmt" + "net/http" - "github.com/hashicorp/consul/api" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" + capi "github.com/hashicorp/consul/api" + "k8s.io/api/admission/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. var servicedefaultslog = logf.Log.WithName("servicedefaults-resource") -// todo: use our own validating webhook so we can inject this properly -var ConsulClient *api.Client -var KubeClient client.Client - -func (r *ServiceDefaults) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -// +kubebuilder:webhook:path=/mutate-consul-hashicorp-com-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,verbs=create;update,versions=v1alpha1,name=mservicedefaults.kb.io - -var _ webhook.Defaulter = &ServiceDefaults{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *ServiceDefaults) Default() { - servicedefaultslog.Info("default", "name", r.Name) - - // TODO(user): fill in your defaulting logic. +type ServiceDefaultsValidator struct { + client.Client + ConsulClient *capi.Client + decoder *admission.Decoder } -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. // +kubebuilder:webhook:verbs=create;update,path=/validate-consul-hashicorp-com-v1alpha1-servicedefaults,mutating=false,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=vservicedefaults.kb.io -var _ webhook.Validator = &ServiceDefaults{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *ServiceDefaults) ValidateCreate() error { - servicedefaultslog.Info("validate create", "name", r.Name) - var svcDefaultsList ServiceDefaultsList - if err := KubeClient.List(context.Background(), &svcDefaultsList); err != nil { - return err +func (v *ServiceDefaultsValidator) Handle(ctx context.Context, req admission.Request) admission.Response { + svcDefaults := &ServiceDefaults{} + err := v.decoder.Decode(req, svcDefaults) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) } - for _, item := range svcDefaultsList.Items { - if item.Name == r.Name { - return fmt.Errorf("ServiceDefaults resource with name %q is already defined in namespace %q – all ServiceDefaults resources must have unique names across namespaces", - r.Name, item.Namespace) + + if req.Operation == v1beta1.Create { + servicedefaultslog.Info("validate create", "name", svcDefaults.Name) + var svcDefaultsList ServiceDefaultsList + if err := v.Client.List(context.Background(), &svcDefaultsList); err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + for _, item := range svcDefaultsList.Items { + if item.Name == svcDefaults.Name { + return admission.Errored(http.StatusBadRequest, fmt.Errorf("ServiceDefaults resource with name %q is already defined in namespace %q – all ServiceDefaults resources must have unique names across namespaces", + svcDefaults.Name, item.Namespace)) + } } } - return nil -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *ServiceDefaults) ValidateUpdate(old runtime.Object) error { - servicedefaultslog.Info("validate update", "name", r.Name) - - // TODO(user): fill in your validation logic upon object update. - return nil + return admission.Allowed("Valid Service Defaults Request") } -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *ServiceDefaults) ValidateDelete() error { - servicedefaultslog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. +func (v *ServiceDefaultsValidator) InjectDecoder(d *admission.Decoder) error { + v.decoder = d return nil } diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml index 7e79bf9955..bd4216ed5c 100644 --- a/config/default/webhookcainjection_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -1,11 +1,5 @@ # This patch add annotation to admission webhook config and # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -metadata: - name: mutating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) --- apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 8b71461da8..957b423a0a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -6,3 +6,4 @@ kind: Kustomization images: - name: controller newName: ashwinvenkatesh/consul-k8s + newTag: latest diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml index 25e21e3c96..e809f78208 100644 --- a/config/webhook/kustomizeconfig.yaml +++ b/config/webhook/kustomizeconfig.yaml @@ -4,18 +4,11 @@ nameReference: - kind: Service version: v1 fieldSpecs: - - kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - kind: ValidatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/name namespace: -- kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true - kind: ValidatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/namespace diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 00639010ba..a2b541022a 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -1,30 +1,4 @@ ---- -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -metadata: - creationTimestamp: null - name: mutating-webhook-configuration -webhooks: -- clientConfig: - caBundle: Cg== - service: - name: webhook-service - namespace: system - path: /mutate-consul-hashicorp-com-v1alpha1-servicedefaults - failurePolicy: Fail - name: mservicedefaults.kb.io - rules: - - apiGroups: - - consul.hashicorp.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - servicedefaults - --- apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 267a64670e..00ef569ceb 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -13,6 +13,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) type Command struct { @@ -101,13 +102,11 @@ func (c *Command) Run(args []string) int { } // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. // Instead we should implement our own validating webhooks so we can pass in the clients. - v1alpha1.ConsulClient = consulClient - v1alpha1.KubeClient = mgr.GetClient() if os.Getenv("ENABLE_WEBHOOKS") != "false" { - if err = (&v1alpha1.ServiceDefaults{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "ServiceDefaults") - os.Exit(1) - } + mgr.GetWebhookServer().Register("/validate-consul-hashicorp-com-v1alpha1-servicedefaults", &webhook.Admission{Handler: &v1alpha1.ServiceDefaultsValidator{ + Client: mgr.GetClient(), + ConsulClient: consulClient, + }}) } // +kubebuilder:scaffold:builder From b0d3e58bc73e566a477b4704b5bbc8d6ccef2e6f Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Mon, 24 Aug 2020 18:07:18 -0400 Subject: [PATCH 10/20] Update webhook to be mutating webhook to imporove flexibility --- api/v1alpha1/servicedefaults_webhook.go | 2 +- config/default/webhookcainjection_patch.yaml | 6 ++++++ config/webhook/kustomizeconfig.yaml | 7 +++++++ config/webhook/manifests.yaml | 8 ++++---- subcommand/controller/command.go | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/api/v1alpha1/servicedefaults_webhook.go b/api/v1alpha1/servicedefaults_webhook.go index 0722954cea..c6cb19a1ed 100644 --- a/api/v1alpha1/servicedefaults_webhook.go +++ b/api/v1alpha1/servicedefaults_webhook.go @@ -21,7 +21,7 @@ type ServiceDefaultsValidator struct { decoder *admission.Decoder } -// +kubebuilder:webhook:verbs=create;update,path=/validate-consul-hashicorp-com-v1alpha1-servicedefaults,mutating=false,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=vservicedefaults.kb.io +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=mservicedefaults.consul.io func (v *ServiceDefaultsValidator) Handle(ctx context.Context, req admission.Request) admission.Response { svcDefaults := &ServiceDefaults{} diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml index bd4216ed5c..7e79bf9955 100644 --- a/config/default/webhookcainjection_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -1,5 +1,11 @@ # This patch add annotation to admission webhook config and # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) --- apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml index e809f78208..25e21e3c96 100644 --- a/config/webhook/kustomizeconfig.yaml +++ b/config/webhook/kustomizeconfig.yaml @@ -4,11 +4,18 @@ nameReference: - kind: Service version: v1 fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name - kind: ValidatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/name namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true - kind: ValidatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/namespace diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index a2b541022a..9e62469bb2 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -1,19 +1,19 @@ --- apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration +kind: MutatingWebhookConfiguration metadata: creationTimestamp: null - name: validating-webhook-configuration + name: mutating-webhook-configuration webhooks: - clientConfig: caBundle: Cg== service: name: webhook-service namespace: system - path: /validate-consul-hashicorp-com-v1alpha1-servicedefaults + path: /mutate-v1alpha1-servicedefaults failurePolicy: Fail - name: vservicedefaults.kb.io + name: mservicedefaults.consul.io rules: - apiGroups: - consul.hashicorp.com diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 00ef569ceb..059b079fe7 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -103,7 +103,7 @@ func (c *Command) Run(args []string) int { // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. // Instead we should implement our own validating webhooks so we can pass in the clients. if os.Getenv("ENABLE_WEBHOOKS") != "false" { - mgr.GetWebhookServer().Register("/validate-consul-hashicorp-com-v1alpha1-servicedefaults", &webhook.Admission{Handler: &v1alpha1.ServiceDefaultsValidator{ + mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", &webhook.Admission{Handler: &v1alpha1.ServiceDefaultsValidator{ Client: mgr.GetClient(), ConsulClient: consulClient, }}) From d6e199a89e5f1af7cd5ca30c19e9b37b260ff0c7 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Tue, 25 Aug 2020 10:15:50 -0400 Subject: [PATCH 11/20] Add tests for ToConsul --- api/v1alpha1/servicedefaults_types.go | 55 +++---- api/v1alpha1/servicedefaults_types_test.go | 159 +++++++++++++++++++++ api/v1alpha1/types.go | 33 +++++ 3 files changed, 222 insertions(+), 25 deletions(-) create mode 100644 api/v1alpha1/servicedefaults_types_test.go diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go index b05a1b9306..e3cf44dee7 100644 --- a/api/v1alpha1/servicedefaults_types.go +++ b/api/v1alpha1/servicedefaults_types.go @@ -54,47 +54,52 @@ func (s *ServiceDefaults) ToConsul() *consulapi.ServiceConfigEntry { //Namespace: s.Namespace, // todo: don't set this unless enterprise Protocol: s.Spec.Protocol, MeshGateway: consulapi.MeshGatewayConfig{ - Mode: consulapi.MeshGatewayModeDefault, //this will change. forcing it to default for now. + Mode: s.gatewayMode(), }, Expose: consulapi.ExposeConfig{ Checks: s.Spec.Expose.Checks, - Paths: []consulapi.ExposePath{}, //will create a helper on our expose paths to translate to consul expose paths + Paths: s.parseExposePath(), }, ExternalSNI: s.Spec.ExternalSNI, } } +func (s *ServiceDefaults) parseExposePath() []consulapi.ExposePath { + var paths []consulapi.ExposePath + for _, path := range s.Spec.Expose.Paths { + paths = append(paths, consulapi.ExposePath{ + ListenerPort: path.ListenerPort, + Path: path.Path, + LocalPathPort: path.LocalPathPort, + Protocol: path.Protocol, + ParsedFromCheck: path.ParsedFromCheck, + }) + } + return paths +} + +func (s *ServiceDefaults) gatewayMode() consulapi.MeshGatewayMode { + switch s.Spec.MeshGateway.Mode { + case "local": + return consulapi.MeshGatewayModeLocal + case "none": + return consulapi.MeshGatewayModeNone + case "remote": + return consulapi.MeshGatewayModeRemote + default: + return consulapi.MeshGatewayModeDefault + } +} + // this will check if the consul struct shares the same spec as the spec of the resource func (s *ServiceDefaults) MatchesConsul(entry *consulapi.ServiceConfigEntry) bool { matches := s.Name == entry.GetName() && s.Spec.Protocol == entry.Protocol && s.Spec.MeshGateway.Mode == string(entry.MeshGateway.Mode) && - s.Spec.Expose.Checks == entry.Expose.Checks && + s.Spec.Expose.Matches(entry.Expose) && s.Spec.ExternalSNI == entry.ExternalSNI if !matches { return false } - - // Also check each exposed path config. - if len(s.Spec.Expose.Paths) != len(entry.Expose.Paths) { - return false - } - for _, path := range s.Spec.Expose.Paths { - found := false - for _, entryPath := range entry.Expose.Paths { - if path.ParsedFromCheck == entryPath.ParsedFromCheck && - path.Protocol == entryPath.Protocol && - path.Path == entryPath.Path && - path.ListenerPort == entryPath.ListenerPort && - path.LocalPathPort == entryPath.LocalPathPort { - found = true - break - } - } - - if !found { - return false - } - } return true } diff --git a/api/v1alpha1/servicedefaults_types_test.go b/api/v1alpha1/servicedefaults_types_test.go new file mode 100644 index 0000000000..ee94adc8f3 --- /dev/null +++ b/api/v1alpha1/servicedefaults_types_test.go @@ -0,0 +1,159 @@ +package v1alpha1 + +import ( + "testing" + + capi "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestRun_ToConsul(t *testing.T) { + cases := []ToConsulCase{ + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "remote", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, + }, + }, + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + } + + for _, testCase := range cases { + output := testCase.input.ToConsul() + require.Equal(t, testCase.expected, output) + } +} + +type ToConsulCase struct { + input *ServiceDefaults + expected *capi.ServiceConfigEntry +} diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index 2f1a433e62..8428b87ad1 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -1,5 +1,9 @@ package v1alpha1 +import ( + "github.com/hashicorp/consul/api" +) + type MeshGatewayMode string const ( @@ -39,6 +43,35 @@ type ExposeConfig struct { Paths []ExposePath `json:"paths,omitempty"` } +func (in *ExposeConfig) Matches(expose api.ExposeConfig) bool { + if in.Checks != expose.Checks { + return false + } + + if len(in.Paths) != len(expose.Paths) { + return false + } + + for _, path := range in.Paths { + found := false + for _, entryPath := range expose.Paths { + if path.ParsedFromCheck == entryPath.ParsedFromCheck && + path.Protocol == entryPath.Protocol && + path.Path == entryPath.Path && + path.ListenerPort == entryPath.ListenerPort && + path.LocalPathPort == entryPath.LocalPathPort { + found = true + break + } + } + + if !found { + return false + } + } + return true +} + type ExposePath struct { // ListenerPort defines the port of the proxy's listener for exposed paths. ListenerPort int `json:"listenerPort,omitempty"` From 719a81a97083f77233d7f6ab1710d4c6da9d7290 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Tue, 25 Aug 2020 10:58:07 -0400 Subject: [PATCH 12/20] Add tests for MatchesConsul --- api/v1alpha1/servicedefaults_types_test.go | 296 +++++++++++++++++++++ subcommand/controller/command.go | 5 +- 2 files changed, 298 insertions(+), 3 deletions(-) diff --git a/api/v1alpha1/servicedefaults_types_test.go b/api/v1alpha1/servicedefaults_types_test.go index ee94adc8f3..687fc40dea 100644 --- a/api/v1alpha1/servicedefaults_types_test.go +++ b/api/v1alpha1/servicedefaults_types_test.go @@ -153,7 +153,303 @@ func TestRun_ToConsul(t *testing.T) { } } +func TestRun_MatchesConsul(t *testing.T) { + cases := []MatchesConsulCase{ + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "remote", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, + }, + true, + }, + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeDefault, + }, + }, + true, + }, + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + true, + }, + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + true, + }, + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + false, + }, + { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + ParsedFromCheck: true, + }, + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + ParsedFromCheck: false, + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + true, + }, + } + + for _, testCase := range cases { + result := testCase.internal.MatchesConsul(testCase.consul) + require.Equal(t, testCase.matches, result) + } +} + type ToConsulCase struct { input *ServiceDefaults expected *capi.ServiceConfigEntry } + +type MatchesConsulCase struct { + internal *ServiceDefaults + consul *capi.ServiceConfigEntry + matches bool +} diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 059b079fe7..d646e043b4 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -63,7 +63,7 @@ func (c *Command) init() { c.help = flags.Usage(help, c.flags) } -func (c *Command) Run(args []string) int { +func (c *Command) Run(_ []string) int { c.once.Do(c.init) if err := c.flags.Parse(nil); err != nil { @@ -100,8 +100,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", "ServiceDefaults") return 1 } - // todo: this is super hacky. Setting global variable so the webhook validation can use the clients. - // Instead we should implement our own validating webhooks so we can pass in the clients. + if os.Getenv("ENABLE_WEBHOOKS") != "false" { mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", &webhook.Admission{Handler: &v1alpha1.ServiceDefaultsValidator{ Client: mgr.GetClient(), From 43b97dec83373ad099b0179d91da1469285fd6e7 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Tue, 25 Aug 2020 16:29:26 -0400 Subject: [PATCH 13/20] Update api/v1alpha1/servicedefaults_types.go Co-authored-by: Luke Kysow <1034429+lkysow@users.noreply.github.com> --- api/v1alpha1/servicedefaults_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go index e3cf44dee7..7543ec7803 100644 --- a/api/v1alpha1/servicedefaults_types.go +++ b/api/v1alpha1/servicedefaults_types.go @@ -91,7 +91,7 @@ func (s *ServiceDefaults) gatewayMode() consulapi.MeshGatewayMode { } } -// this will check if the consul struct shares the same spec as the spec of the resource +// MatchesConsul returns true if entry has the same config as this struct. func (s *ServiceDefaults) MatchesConsul(entry *consulapi.ServiceConfigEntry) bool { matches := s.Name == entry.GetName() && s.Spec.Protocol == entry.Protocol && From 237de0ddcd7b619cfd60aa1f06937dde83db683f Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Wed, 26 Aug 2020 09:37:45 -0400 Subject: [PATCH 14/20] Apply suggestions from code review Co-authored-by: Luke Kysow <1034429+lkysow@users.noreply.github.com> --- api/v1alpha1/servicedefaults_types.go | 6 +----- api/v1alpha1/servicedefaults_types_test.go | 2 +- controllers/servicedefaults_controller.go | 9 +++++---- subcommand/controller/command.go | 4 ++-- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go index 7543ec7803..9d6111032c 100644 --- a/api/v1alpha1/servicedefaults_types.go +++ b/api/v1alpha1/servicedefaults_types.go @@ -93,13 +93,9 @@ func (s *ServiceDefaults) gatewayMode() consulapi.MeshGatewayMode { // MatchesConsul returns true if entry has the same config as this struct. func (s *ServiceDefaults) MatchesConsul(entry *consulapi.ServiceConfigEntry) bool { - matches := s.Name == entry.GetName() && + return s.Name == entry.GetName() && s.Spec.Protocol == entry.Protocol && s.Spec.MeshGateway.Mode == string(entry.MeshGateway.Mode) && s.Spec.Expose.Matches(entry.Expose) && s.Spec.ExternalSNI == entry.ExternalSNI - if !matches { - return false - } - return true } diff --git a/api/v1alpha1/servicedefaults_types_test.go b/api/v1alpha1/servicedefaults_types_test.go index 687fc40dea..c2f6f8e59d 100644 --- a/api/v1alpha1/servicedefaults_types_test.go +++ b/api/v1alpha1/servicedefaults_types_test.go @@ -8,7 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestRun_ToConsul(t *testing.T) { +func ToConsul(t *testing.T) { cases := []ToConsulCase{ { &ServiceDefaults{ diff --git a/controllers/servicedefaults_controller.go b/controllers/servicedefaults_controller.go index d73139f099..f206002c2f 100644 --- a/controllers/servicedefaults_controller.go +++ b/controllers/servicedefaults_controller.go @@ -43,7 +43,7 @@ func (r *ServiceDefaultsReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er if k8serr.IsNotFound(err) { return ctrl.Result{}, client.IgnoreNotFound(err) } else if err != nil { - logger.Error(err, "failed to retrieve Service Default") + logger.Error(err, "failed to retrieve resource") return ctrl.Result{}, err } @@ -62,7 +62,7 @@ func (r *ServiceDefaultsReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er // The object is being deleted if containsString(svcDefaults.ObjectMeta.Finalizers, FinalizerName) { logger.Info("deletion event") - // our finalizer is present, so we need to delete the config entry + // Our finalizer is present, so we need to delete the config entry // from consul. _, err = r.ConsulClient.ConfigEntries().Delete(capi.ServiceDefaults, svcDefaults.Name, nil) if err != nil { @@ -193,7 +193,7 @@ func isNotFoundErr(err error) bool { return err != nil && strings.Contains(err.Error(), "404") } -// Helper functions to check and remove string from a slice of strings. +// containsString returns true if s is in slice. func containsString(slice []string, s string) bool { for _, item := range slice { if item == s { @@ -203,7 +203,8 @@ func containsString(slice []string, s string) bool { return false } -func removeString(slice []string, s string) (result []string) { +// removeString removes s from slice and returns the new slice. +func removeString(slice []string, s string) []string { for _, item := range slice { if item == s { continue diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index d646e043b4..81d00c144f 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -56,7 +56,7 @@ func (c *Command) init() { c.flags = flag.NewFlagSet("", flag.ContinueOnError) c.flags.StringVar(&c.metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") c.flags.BoolVar(&c.enableLeaderElection, "enable-leader-election", false, - "Enable leader election for controller manager. "+ + "Enable leader election for controller. "+ "Enabling this will ensure there is only one active controller manager.") c.httpFlags = &flags.HTTPFlags{} flags.Merge(c.flags, c.httpFlags.Flags()) @@ -78,7 +78,7 @@ func (c *Command) Run(_ []string) int { MetricsBindAddress: c.metricsAddr, Port: 9443, LeaderElection: c.enableLeaderElection, - LeaderElectionID: "65a0bb41.my.domain", + LeaderElectionID: "consul.hashicorp.com", }) if err != nil { setupLog.Error(err, "unable to start manager") From 41854606c3bd27f500f80a92a36f1ea6aefdb35d Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Wed, 26 Aug 2020 09:40:03 -0400 Subject: [PATCH 15/20] Add test for webhook and fix some code review comments --- api/v1alpha1/servicedefaults_webhook.go | 13 ++- api/v1alpha1/servicedefaults_webhook_test.go | 97 ++++++++++++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 2 +- subcommand/controller/command.go | 15 +-- 4 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 api/v1alpha1/servicedefaults_webhook_test.go diff --git a/api/v1alpha1/servicedefaults_webhook.go b/api/v1alpha1/servicedefaults_webhook.go index c6cb19a1ed..61435cce73 100644 --- a/api/v1alpha1/servicedefaults_webhook.go +++ b/api/v1alpha1/servicedefaults_webhook.go @@ -15,7 +15,14 @@ import ( // log is for logging in this package. var servicedefaultslog = logf.Log.WithName("servicedefaults-resource") -type ServiceDefaultsValidator struct { +func NewServiceDefaultsValidator(client client.Client, consulClient *capi.Client) *serviceDefaultsValidator { + return &serviceDefaultsValidator{ + Client: client, + ConsulClient: consulClient, + } +} + +type serviceDefaultsValidator struct { client.Client ConsulClient *capi.Client decoder *admission.Decoder @@ -23,7 +30,7 @@ type ServiceDefaultsValidator struct { // +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=mservicedefaults.consul.io -func (v *ServiceDefaultsValidator) Handle(ctx context.Context, req admission.Request) admission.Response { +func (v *serviceDefaultsValidator) Handle(ctx context.Context, req admission.Request) admission.Response { svcDefaults := &ServiceDefaults{} err := v.decoder.Decode(req, svcDefaults) if err != nil { @@ -46,7 +53,7 @@ func (v *ServiceDefaultsValidator) Handle(ctx context.Context, req admission.Req return admission.Allowed("Valid Service Defaults Request") } -func (v *ServiceDefaultsValidator) InjectDecoder(d *admission.Decoder) error { +func (v *serviceDefaultsValidator) InjectDecoder(d *admission.Decoder) error { v.decoder = d return nil } diff --git a/api/v1alpha1/servicedefaults_webhook_test.go b/api/v1alpha1/servicedefaults_webhook_test.go new file mode 100644 index 0000000000..80431ca398 --- /dev/null +++ b/api/v1alpha1/servicedefaults_webhook_test.go @@ -0,0 +1,97 @@ +package v1alpha1 + +import ( + "context" + "encoding/json" + "testing" + + capi "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil" + "github.com/stretchr/testify/require" + "k8s.io/api/admission/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +func TestRun_HandleErrorsIfServiceDefaultsWithSameNameExists(t *testing.T) { + svcDefaults := &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + }, + } + s := scheme.Scheme + s.AddKnownTypes(GroupVersion, svcDefaults) + s.AddKnownTypes(GroupVersion, &ServiceDefaultsList{}) + ctx := context.Background() + + consul, err := testutil.NewTestServerConfigT(t, nil) + require.NoError(t, err) + defer consul.Stop() + consulClient, err := capi.NewClient(&capi.Config{ + Address: consul.HTTPAddr, + }) + require.NoError(t, err) + + client := fake.NewFakeClientWithScheme(s, svcDefaults) + + validator := &serviceDefaultsValidator{ + Client: client, + ConsulClient: consulClient, + } + + decoder, err := admission.NewDecoder(scheme.Scheme) + require.NoError(t, err) + err = validator.InjectDecoder(decoder) + require.NoError(t, err) + + requestObject := &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "other-namespace", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + }, + } + marshalledRequestObject, err := json.Marshal(requestObject) + require.NoError(t, err) + + response := validator.Handle(ctx, admission.Request{ + AdmissionRequest: v1beta1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Kind: "servicedefaults", + }, + Resource: metav1.GroupVersionResource{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Resource: "servicedefaults", + }, + RequestKind: &metav1.GroupVersionKind{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Kind: "servicedefaults", + }, + RequestResource: &metav1.GroupVersionResource{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Resource: "servicedefaults", + }, + Name: "foo", + Namespace: "other-namespace", + Operation: v1beta1.Create, + Object: runtime.RawExtension{ + Raw: marshalledRequestObject, + }, + }, + }) + require.False(t, response.Allowed) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 54a8d3aedf..c51eab763c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha1 import ( - "k8s.io/apimachinery/pkg/runtime" + runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 81d00c144f..4298aa33ac 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -22,11 +22,6 @@ type Command struct { httpFlags *flags.HTTPFlags metricsAddr string enableLeaderElection bool - flagSecretName string - flagInitType string - flagNamespace string - flagACLDir string - flagTokenSinkFile string once sync.Once help string @@ -38,7 +33,7 @@ func (c *Command) Help() string { } func (c *Command) Synopsis() string { - return help + return synopsis } var ( @@ -102,10 +97,7 @@ func (c *Command) Run(_ []string) int { } if os.Getenv("ENABLE_WEBHOOKS") != "false" { - mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", &webhook.Admission{Handler: &v1alpha1.ServiceDefaultsValidator{ - Client: mgr.GetClient(), - ConsulClient: consulClient, - }}) + mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", &webhook.Admission{Handler: v1alpha1.NewServiceDefaultsValidator(mgr.GetClient(), consulClient)}) } // +kubebuilder:scaffold:builder @@ -117,9 +109,10 @@ func (c *Command) Run(_ []string) int { return 0 } +const synopsis = "Starts the Consul Kubernetes controller" const help = ` Usage: consul-k8s controller [options] - Starts the consul kubernetes controller + Starts the Consul Kubernetes controller that manages Consul Custom Resource Definitions ` From 5c2987b648085a5a0e179fcb4ffb062a3b85785b Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Wed, 26 Aug 2020 14:13:05 -0400 Subject: [PATCH 16/20] Address PR comments --- api/v1alpha1/servicedefaults_types.go | 42 +- api/v1alpha1/servicedefaults_types_test.go | 686 ++++++++++++++---- api/v1alpha1/servicedefaults_webhook.go | 20 +- api/v1alpha1/types.go | 84 ++- commands.go | 2 +- .../consul.hashicorp.com_servicedefaults.yaml | 4 - config/prometheus/kustomization.yaml | 2 - config/prometheus/monitor.yaml | 16 - controllers/servicedefaults_controller.go | 55 +- controllers/suite_test.go | 66 -- subcommand/controller/command.go | 59 +- 11 files changed, 696 insertions(+), 340 deletions(-) delete mode 100644 config/prometheus/kustomization.yaml delete mode 100644 config/prometheus/monitor.yaml delete mode 100644 controllers/suite_test.go diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go index 9d6111032c..830198bb57 100644 --- a/api/v1alpha1/servicedefaults_types.go +++ b/api/v1alpha1/servicedefaults_types.go @@ -5,9 +5,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // ServiceDefaultsSpec defines the desired state of ServiceDefaults type ServiceDefaultsSpec struct { Protocol string `json:"protocol,omitempty"` @@ -47,50 +44,19 @@ func init() { SchemeBuilder.Register(&ServiceDefaults{}, &ServiceDefaultsList{}) } +// ToConsul converts the entry into it's Consul equivalent struct. func (s *ServiceDefaults) ToConsul() *consulapi.ServiceConfigEntry { return &consulapi.ServiceConfigEntry{ Kind: consulapi.ServiceDefaults, Name: s.Name, //Namespace: s.Namespace, // todo: don't set this unless enterprise - Protocol: s.Spec.Protocol, - MeshGateway: consulapi.MeshGatewayConfig{ - Mode: s.gatewayMode(), - }, - Expose: consulapi.ExposeConfig{ - Checks: s.Spec.Expose.Checks, - Paths: s.parseExposePath(), - }, + Protocol: s.Spec.Protocol, + MeshGateway: s.Spec.MeshGateway.ToConsul(), + Expose: s.Spec.Expose.ToConsul(), ExternalSNI: s.Spec.ExternalSNI, } } -func (s *ServiceDefaults) parseExposePath() []consulapi.ExposePath { - var paths []consulapi.ExposePath - for _, path := range s.Spec.Expose.Paths { - paths = append(paths, consulapi.ExposePath{ - ListenerPort: path.ListenerPort, - Path: path.Path, - LocalPathPort: path.LocalPathPort, - Protocol: path.Protocol, - ParsedFromCheck: path.ParsedFromCheck, - }) - } - return paths -} - -func (s *ServiceDefaults) gatewayMode() consulapi.MeshGatewayMode { - switch s.Spec.MeshGateway.Mode { - case "local": - return consulapi.MeshGatewayModeLocal - case "none": - return consulapi.MeshGatewayModeNone - case "remote": - return consulapi.MeshGatewayModeRemote - default: - return consulapi.MeshGatewayModeDefault - } -} - // MatchesConsul returns true if entry has the same config as this struct. func (s *ServiceDefaults) MatchesConsul(entry *consulapi.ServiceConfigEntry) bool { return s.Name == entry.GetName() && diff --git a/api/v1alpha1/servicedefaults_types_test.go b/api/v1alpha1/servicedefaults_types_test.go index c2f6f8e59d..3833e7dbce 100644 --- a/api/v1alpha1/servicedefaults_types_test.go +++ b/api/v1alpha1/servicedefaults_types_test.go @@ -8,9 +8,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func ToConsul(t *testing.T) { - cases := []ToConsulCase{ - { +func Test_ToConsul(t *testing.T) { + cases := map[string]struct { + input *ServiceDefaults + expected *capi.ServiceConfigEntry + }{ + "protocol:http,mode:remote": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -33,7 +36,7 @@ func ToConsul(t *testing.T) { }, }, }, - { + "protocol:https,mode:local,exposePaths:1,externalSNI": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -48,11 +51,10 @@ func ToConsul(t *testing.T) { Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, }, }, @@ -71,44 +73,41 @@ func ToConsul(t *testing.T) { Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, }, }, ExternalSNI: "test-external-sni", }, }, - { + "protocol:\"\",mode:\"\",exposePaths:2,externalSNI": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", Namespace: "consul-configs", }, Spec: ServiceDefaultsSpec{ - Protocol: "https", + Protocol: "", MeshGateway: MeshGatewayConfig{ - Mode: "local", + Mode: "", }, Expose: ExposeConfig{ Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", }, }, }, @@ -119,43 +118,115 @@ func ToConsul(t *testing.T) { Kind: capi.ServiceDefaults, Name: "my-test-service", Namespace: "", - Protocol: "https", + Protocol: "", MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, + Mode: capi.MeshGatewayModeDefault, }, Expose: capi.ExposeConfig{ Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", }, }, }, ExternalSNI: "test-external-sni", }, }, + "protocol:http,mode:none,exposePaths:1,exposeChecks:false": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "none", + }, + Expose: ExposeConfig{ + Checks: false, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeNone, + }, + Expose: capi.ExposeConfig{ + Checks: false, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + }, + }, + "protocol:https,mode:unsupported": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "unsupported", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeDefault, + }, + }, + }, } - for _, testCase := range cases { - output := testCase.input.ToConsul() - require.Equal(t, testCase.expected, output) + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + output := testCase.input.ToConsul() + require.Equal(t, testCase.expected, output) + }) } } -func TestRun_MatchesConsul(t *testing.T) { - cases := []MatchesConsulCase{ - { +func Test_MatchesConsul(t *testing.T) { + cases := map[string]struct { + internal *ServiceDefaults + consul *capi.ServiceConfigEntry + matches bool + }{ + "name:matches": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -179,7 +250,31 @@ func TestRun_MatchesConsul(t *testing.T) { }, true, }, - { + "name:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "remote", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "differently-named-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, + }, + false, + }, + "protocol:matches": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -200,7 +295,128 @@ func TestRun_MatchesConsul(t *testing.T) { }, true, }, - { + "protocol:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeDefault, + }, + }, + false, + }, + "gatewayConfig:matches": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "remote", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, + }, + true, + }, + "gatewayConfig:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "remote", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + }, + false, + }, + "externalSNI:matches": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "remote", + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, + ExternalSNI: "test-external-sni", + }, + true, + }, + "externalSNI:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "http", + MeshGateway: MeshGatewayConfig{ + Mode: "remote", + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "http", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + ExternalSNI: "different-external-sni", + }, + false, + }, + "expose.checks:matches": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -215,11 +431,10 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, }, }, @@ -238,11 +453,10 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, }, }, @@ -250,7 +464,55 @@ func TestRun_MatchesConsul(t *testing.T) { }, true, }, - { + "expose.checks:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-config", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: false, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + ExternalSNI: "test-external-sni", + }, + false, + }, + "expose.paths:matches": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -265,22 +527,19 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", }, }, }, - ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ @@ -295,26 +554,23 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", }, }, }, - ExternalSNI: "test-external-sni", }, true, }, - { + "expose.paths.listenerPort:mismatched": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -329,18 +585,200 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []ExposePath{ { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, + }, + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 81, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + }, + false, + }, + "expose.paths.path:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/differnt/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + }, + false, + }, + "expose.paths.localPathPort:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 21, + Protocol: "tcp", + }, + }, + }, + }, + false, + }, + "expose.paths.protocol:mismatched": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", + }, + }, + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "my-test-service", + Namespace: "", + Protocol: "https", + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + }, + Expose: capi.ExposeConfig{ + Checks: true, + Paths: []capi.ExposePath{ + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "https", + }, + }, + }, + }, + false, + }, + "expose.paths:mismatched when path lengths are different": { + &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + Namespace: "consul-configs", + }, + Spec: ServiceDefaultsSpec{ + Protocol: "https", + MeshGateway: MeshGatewayConfig{ + Mode: "local", + }, + Expose: ExposeConfig{ + Checks: true, + Paths: []ExposePath{ + { + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", + }, + { + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, }, }, @@ -359,11 +797,10 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", }, }, }, @@ -371,7 +808,7 @@ func TestRun_MatchesConsul(t *testing.T) { }, false, }, - { + "expose.paths:match when paths orders are different": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "my-test-service", @@ -386,18 +823,16 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []ExposePath{ { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", }, { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, }, }, @@ -416,18 +851,16 @@ func TestRun_MatchesConsul(t *testing.T) { Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - ParsedFromCheck: true, + ListenerPort: 80, + Path: "/test/path", + LocalPathPort: 42, + Protocol: "tcp", }, { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - ParsedFromCheck: false, + ListenerPort: 8080, + Path: "/second/test/path", + LocalPathPort: 11, + Protocol: "https", }, }, }, @@ -437,19 +870,10 @@ func TestRun_MatchesConsul(t *testing.T) { }, } - for _, testCase := range cases { - result := testCase.internal.MatchesConsul(testCase.consul) - require.Equal(t, testCase.matches, result) + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + result := testCase.internal.MatchesConsul(testCase.consul) + require.Equal(t, testCase.matches, result) + }) } } - -type ToConsulCase struct { - input *ServiceDefaults - expected *capi.ServiceConfigEntry -} - -type MatchesConsulCase struct { - internal *ServiceDefaults - consul *capi.ServiceConfigEntry - matches bool -} diff --git a/api/v1alpha1/servicedefaults_webhook.go b/api/v1alpha1/servicedefaults_webhook.go index 61435cce73..97310cad28 100644 --- a/api/v1alpha1/servicedefaults_webhook.go +++ b/api/v1alpha1/servicedefaults_webhook.go @@ -5,48 +5,48 @@ import ( "fmt" "net/http" + "github.com/go-logr/logr" capi "github.com/hashicorp/consul/api" "k8s.io/api/admission/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) -// log is for logging in this package. -var servicedefaultslog = logf.Log.WithName("servicedefaults-resource") - -func NewServiceDefaultsValidator(client client.Client, consulClient *capi.Client) *serviceDefaultsValidator { +func NewServiceDefaultsValidator(client client.Client, consulClient *capi.Client, logger logr.Logger) *serviceDefaultsValidator { return &serviceDefaultsValidator{ Client: client, ConsulClient: consulClient, + Logger: logger, } } type serviceDefaultsValidator struct { client.Client ConsulClient *capi.Client + Logger logr.Logger decoder *admission.Decoder } +// Note: The path value in the below line is the path to the webhook. If it is updates, run code-gen, update subcommand/controller/command.go and the consul-helm value for the path to the webhook. // +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=mservicedefaults.consul.io func (v *serviceDefaultsValidator) Handle(ctx context.Context, req admission.Request) admission.Response { - svcDefaults := &ServiceDefaults{} - err := v.decoder.Decode(req, svcDefaults) + var svcDefaults ServiceDefaults + err := v.decoder.Decode(req, &svcDefaults) if err != nil { return admission.Errored(http.StatusBadRequest, err) } if req.Operation == v1beta1.Create { - servicedefaultslog.Info("validate create", "name", svcDefaults.Name) + v.Logger.Info("validate create", "name", svcDefaults.Name) var svcDefaultsList ServiceDefaultsList if err := v.Client.List(context.Background(), &svcDefaultsList); err != nil { return admission.Errored(http.StatusInternalServerError, err) } for _, item := range svcDefaultsList.Items { if item.Name == svcDefaults.Name { - return admission.Errored(http.StatusBadRequest, fmt.Errorf("ServiceDefaults resource with name %q is already defined in namespace %q – all ServiceDefaults resources must have unique names across namespaces", - svcDefaults.Name, item.Namespace)) + return admission.Errored(http.StatusBadRequest, fmt.Errorf("ServiceDefaults resource with name %q is already defined – all ServiceDefaults resources must have unique names across namespaces", + svcDefaults.Name)) } } } diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index 8428b87ad1..6581c883e5 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -1,7 +1,7 @@ package v1alpha1 import ( - "github.com/hashicorp/consul/api" + capi "github.com/hashicorp/consul/api" ) type MeshGatewayMode string @@ -32,6 +32,28 @@ type MeshGatewayConfig struct { Mode string `json:"mode,omitempty"` } +//ToConsul returns the MeshGatewayConfig for the entry +func (m MeshGatewayConfig) ToConsul() capi.MeshGatewayConfig { + switch m.Mode { + case string(capi.MeshGatewayModeLocal): + return capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeLocal, + } + case string(capi.MeshGatewayModeNone): + return capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeNone, + } + case string(capi.MeshGatewayModeRemote): + return capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + } + default: + return capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeDefault, + } + } +} + // ExposeConfig describes HTTP paths to expose through Envoy outside of Connect. // Users can expose individual paths and/or all HTTP/GRPC paths for checks. type ExposeConfig struct { @@ -43,20 +65,35 @@ type ExposeConfig struct { Paths []ExposePath `json:"paths,omitempty"` } -func (in *ExposeConfig) Matches(expose api.ExposeConfig) bool { - if in.Checks != expose.Checks { +type ExposePath struct { + // ListenerPort defines the port of the proxy's listener for exposed paths. + ListenerPort int `json:"listenerPort,omitempty"` + + // Path is the path to expose through the proxy, ie. "/metrics." + Path string `json:"path,omitempty"` + + // LocalPathPort is the port that the service is listening on for the given path. + LocalPathPort int `json:"localPathPort,omitempty"` + + // Protocol describes the upstream's service protocol. + // Valid values are "http" and "http2", defaults to "http" + Protocol string `json:"protocol,omitempty"` +} + +//Matches returns true if the expose config of the entry is the same as the struct +func (e ExposeConfig) Matches(expose capi.ExposeConfig) bool { + if e.Checks != expose.Checks { return false } - if len(in.Paths) != len(expose.Paths) { + if len(e.Paths) != len(expose.Paths) { return false } - for _, path := range in.Paths { + for _, path := range e.Paths { found := false for _, entryPath := range expose.Paths { - if path.ParsedFromCheck == entryPath.ParsedFromCheck && - path.Protocol == entryPath.Protocol && + if path.Protocol == entryPath.Protocol && path.Path == entryPath.Path && path.ListenerPort == entryPath.ListenerPort && path.LocalPathPort == entryPath.LocalPathPort { @@ -72,20 +109,23 @@ func (in *ExposeConfig) Matches(expose api.ExposeConfig) bool { return true } -type ExposePath struct { - // ListenerPort defines the port of the proxy's listener for exposed paths. - ListenerPort int `json:"listenerPort,omitempty"` - - // Path is the path to expose through the proxy, ie. "/metrics." - Path string `json:"path,omitempty"` - - // LocalPathPort is the port that the service is listening on for the given path. - LocalPathPort int `json:"localPathPort,omitempty"` - - // Protocol describes the upstream's service protocol. - // Valid values are "http" and "http2", defaults to "http" - Protocol string `json:"protocol,omitempty"` +//ToConsul returns the ExposeConfig for the entry +func (e ExposeConfig) ToConsul() capi.ExposeConfig { + return capi.ExposeConfig{ + Checks: e.Checks, + Paths: e.parseExposePath(), + } +} - // ParsedFromCheck is set if this path was parsed from a registered check - ParsedFromCheck bool `json:"parsedFromCheck,omitempty"` +func (e ExposeConfig) parseExposePath() []capi.ExposePath { + var paths []capi.ExposePath + for _, path := range e.Paths { + paths = append(paths, capi.ExposePath{ + ListenerPort: path.ListenerPort, + Path: path.Path, + LocalPathPort: path.LocalPathPort, + Protocol: path.Protocol, + }) + } + return paths } diff --git a/commands.go b/commands.go index c01d9cc138..8317e3e9e4 100644 --- a/commands.go +++ b/commands.go @@ -66,7 +66,7 @@ func init() { }, "controller": func() (cli.Command, error) { - return &cmdController.Command{}, nil + return &cmdController.Command{UI: ui}, nil }, } } diff --git a/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 4d745010e3..c1cbb35564 100644 --- a/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -63,10 +63,6 @@ spec: description: LocalPathPort is the port that the service is listening on for the given path. type: integer - parsedFromCheck: - description: ParsedFromCheck is set if this path was parsed - from a registered check - type: boolean path: description: Path is the path to expose through the proxy, ie. "/metrics." diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml deleted file mode 100644 index ed137168a1..0000000000 --- a/config/prometheus/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: -- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml deleted file mode 100644 index 9b8047b760..0000000000 --- a/config/prometheus/monitor.yaml +++ /dev/null @@ -1,16 +0,0 @@ - -# Prometheus Monitor Service (Metrics) -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - control-plane: controller-manager - name: controller-manager-metrics-monitor - namespace: system -spec: - endpoints: - - path: /metrics - port: https - selector: - matchLabels: - control-plane: controller-manager diff --git a/controllers/servicedefaults_controller.go b/controllers/servicedefaults_controller.go index f206002c2f..5ea9380d7f 100644 --- a/controllers/servicedefaults_controller.go +++ b/controllers/servicedefaults_controller.go @@ -99,40 +99,44 @@ func (r *ServiceDefaultsReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { return ctrl.Result{}, err } - } else if err != nil { + return ctrl.Result{}, nil + } + + // If there is an error when trying to get the config entry from the api server, fail the reconcile + if err != nil { svcDefaults.Status.Conditions = syncFailed(ConsulAgentError, err.Error()) if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { return ctrl.Result{}, err } return ctrl.Result{}, err - } else { - svcDefaultEntry, ok := entry.(*capi.ServiceConfigEntry) - if !ok { - err := errors.New("could not cast entry as ServiceConfigEntry") - svcDefaults.Status.Conditions = syncUnknownWithError(CastError, err.Error()) - if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { - return ctrl.Result{}, err - } + } + + svcDefaultEntry, ok := entry.(*capi.ServiceConfigEntry) + if !ok { + err := errors.New("could not cast entry as ServiceConfigEntry") + svcDefaults.Status.Conditions = syncUnknownWithError(CastError, err.Error()) + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { return ctrl.Result{}, err } - if !svcDefaults.MatchesConsul(svcDefaultEntry) { - _, _, err := r.ConsulClient.ConfigEntries().Set(svcDefaults.ToConsul(), nil) - if err != nil { - svcDefaults.Status.Conditions = syncUnknownWithError(ConsulAgentError, err.Error()) - if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - svcDefaults.Status.Conditions = syncSuccessful() - if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { - return ctrl.Result{}, err - } - } else if !svcDefaults.Status.GetCondition(consulv1alpha1.ConditionSynced).IsTrue() { - svcDefaults.Status.Conditions = syncSuccessful() + return ctrl.Result{}, err + } + if !svcDefaults.MatchesConsul(svcDefaultEntry) { + _, _, err := r.ConsulClient.ConfigEntries().Set(svcDefaults.ToConsul(), nil) + if err != nil { + svcDefaults.Status.Conditions = syncUnknownWithError(ConsulAgentError, err.Error()) if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { return ctrl.Result{}, err } + return ctrl.Result{}, err + } + svcDefaults.Status.Conditions = syncSuccessful() + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err + } + } else if !svcDefaults.Status.GetCondition(consulv1alpha1.ConditionSynced).IsTrue() { + svcDefaults.Status.Conditions = syncSuccessful() + if err := r.Status().Update(context.Background(), &svcDefaults); err != nil { + return ctrl.Result{}, err } } @@ -205,11 +209,12 @@ func containsString(slice []string, s string) bool { // removeString removes s from slice and returns the new slice. func removeString(slice []string, s string) []string { + var result []string for _, item := range slice { if item == s { continue } result = append(result, item) } - return + return result } diff --git a/controllers/suite_test.go b/controllers/suite_test.go deleted file mode 100644 index d0fcae54a0..0000000000 --- a/controllers/suite_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package controllers - -import ( - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - "sigs.k8s.io/controller-runtime/pkg/envtest/printer" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - consulv1alpha1 "github.com/hashicorp/consul-k8s/api/v1alpha1" - // +kubebuilder:scaffold:imports -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment - -func TestAPIs(t *testing.T) { - t.SkipNow() - RegisterFailHandler(Fail) - - RunSpecsWithDefaultAndCustomReporters(t, - "Controller Suite", - []Reporter{printer.NewlineReporter{}}) -} - -var _ = BeforeSuite(func(done Done) { - logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, - } - - var err error - cfg, err = testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - err = consulv1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) - - close(done) -}, 60) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 4298aa33ac..3031927f63 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -2,12 +2,14 @@ package controller import ( "flag" + "fmt" "os" "sync" "github.com/hashicorp/consul-k8s/api/v1alpha1" "github.com/hashicorp/consul-k8s/controllers" "github.com/hashicorp/consul-k8s/subcommand/flags" + "github.com/mitchellh/cli" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -17,25 +19,18 @@ import ( ) type Command struct { - flags *flag.FlagSet - k8s *flags.K8SFlags - httpFlags *flags.HTTPFlags - metricsAddr string - enableLeaderElection bool + UI cli.Ui + flagSet *flag.FlagSet + k8s *flags.K8SFlags + httpFlags *flags.HTTPFlags + + flagMetricsAddr string + flagEnableLeaderElection bool once sync.Once help string } -func (c *Command) Help() string { - c.once.Do(c.init) - return c.help -} - -func (c *Command) Synopsis() string { - return synopsis -} - var ( scheme = runtime.NewScheme() setupLog = ctrl.Log.WithName("setup") @@ -48,21 +43,24 @@ func init() { } func (c *Command) init() { - c.flags = flag.NewFlagSet("", flag.ContinueOnError) - c.flags.StringVar(&c.metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - c.flags.BoolVar(&c.enableLeaderElection, "enable-leader-election", false, + c.flagSet = flag.NewFlagSet("", flag.ContinueOnError) + c.flagSet.StringVar(&c.flagMetricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + c.flagSet.BoolVar(&c.flagEnableLeaderElection, "enable-leader-election", false, "Enable leader election for controller. "+ "Enabling this will ensure there is only one active controller manager.") c.httpFlags = &flags.HTTPFlags{} - flags.Merge(c.flags, c.httpFlags.Flags()) - c.help = flags.Usage(help, c.flags) + flags.Merge(c.flagSet, c.httpFlags.Flags()) + c.help = flags.Usage(help, c.flagSet) } func (c *Command) Run(_ []string) int { c.once.Do(c.init) - - if err := c.flags.Parse(nil); err != nil { - setupLog.Error(err, "parsing flags") + if err := c.flagSet.Parse(nil); err != nil { + setupLog.Error(err, "parsing flagSet") + return 1 + } + if len(c.flagSet.Args()) > 0 { + c.UI.Error(fmt.Sprintf("Should have no non-flag arguments.")) return 1 } @@ -70,9 +68,9 @@ func (c *Command) Run(_ []string) int { mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, - MetricsBindAddress: c.metricsAddr, + MetricsBindAddress: c.flagMetricsAddr, Port: 9443, - LeaderElection: c.enableLeaderElection, + LeaderElection: c.flagEnableLeaderElection, LeaderElectionID: "consul.hashicorp.com", }) if err != nil { @@ -97,7 +95,9 @@ func (c *Command) Run(_ []string) int { } if os.Getenv("ENABLE_WEBHOOKS") != "false" { - mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", &webhook.Admission{Handler: v1alpha1.NewServiceDefaultsValidator(mgr.GetClient(), consulClient)}) + //Note: The path here should be identical to the one on the kubebuilder annotation in file api/v1alpha1/servicedefaults_webhook.go + mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", + &webhook.Admission{Handler: v1alpha1.NewServiceDefaultsValidator(mgr.GetClient(), consulClient, ctrl.Log.WithName("webhooks").WithName("ServiceDefaults"))}) } // +kubebuilder:scaffold:builder @@ -109,6 +109,15 @@ func (c *Command) Run(_ []string) int { return 0 } +func (c *Command) Help() string { + c.once.Do(c.init) + return c.help +} + +func (c *Command) Synopsis() string { + return synopsis +} + const synopsis = "Starts the Consul Kubernetes controller" const help = ` Usage: consul-k8s controller [options] From a6f4d9a8d518fd8b211062c809b3c0d754f02a4e Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Wed, 26 Aug 2020 15:37:55 -0400 Subject: [PATCH 17/20] update based on feedback on PR --- Makefile | 2 +- PROJECT | 1 + api/v1alpha1/servicedefaults_types.go | 86 +++++++++++++++++-- api/v1alpha1/servicedefaults_webhook_test.go | 2 + api/v1alpha1/types.go | 76 ---------------- build-support/controller/README.md | 9 ++ .../controller}/boilerplate.go.txt | 0 7 files changed, 94 insertions(+), 82 deletions(-) create mode 100644 build-support/controller/README.md rename {hack => build-support/controller}/boilerplate.go.txt (100%) diff --git a/Makefile b/Makefile index 12974a2984..6fa0ba74f1 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,7 @@ ctrl-manifests: controller-gen # Generate code ctrl-generate: controller-gen - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + $(CONTROLLER_GEN) object:headerFile="build-support/controller/boilerplate.go.txt" paths="./..." # find or download controller-gen # download controller-gen if necessary diff --git a/PROJECT b/PROJECT index 931cdad64b..4ebb45c34e 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,4 @@ +# this is a generated file used for operator sdk during code generation of CRDs, Controllers and webhooks domain: hashicorp.com layout: go.kubebuilder.io/v2 repo: github.com/hashicorp/consul-k8s diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go index 830198bb57..3c4286213a 100644 --- a/api/v1alpha1/servicedefaults_types.go +++ b/api/v1alpha1/servicedefaults_types.go @@ -1,7 +1,7 @@ package v1alpha1 import ( - consulapi "github.com/hashicorp/consul/api" + capi "github.com/hashicorp/consul/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -45,9 +45,9 @@ func init() { } // ToConsul converts the entry into it's Consul equivalent struct. -func (s *ServiceDefaults) ToConsul() *consulapi.ServiceConfigEntry { - return &consulapi.ServiceConfigEntry{ - Kind: consulapi.ServiceDefaults, +func (s *ServiceDefaults) ToConsul() *capi.ServiceConfigEntry { + return &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, Name: s.Name, //Namespace: s.Namespace, // todo: don't set this unless enterprise Protocol: s.Spec.Protocol, @@ -58,10 +58,86 @@ func (s *ServiceDefaults) ToConsul() *consulapi.ServiceConfigEntry { } // MatchesConsul returns true if entry has the same config as this struct. -func (s *ServiceDefaults) MatchesConsul(entry *consulapi.ServiceConfigEntry) bool { +func (s *ServiceDefaults) MatchesConsul(entry *capi.ServiceConfigEntry) bool { return s.Name == entry.GetName() && s.Spec.Protocol == entry.Protocol && s.Spec.MeshGateway.Mode == string(entry.MeshGateway.Mode) && s.Spec.Expose.Matches(entry.Expose) && s.Spec.ExternalSNI == entry.ExternalSNI } + +// ExposeConfig describes HTTP paths to expose through Envoy outside of Connect. +// Users can expose individual paths and/or all HTTP/GRPC paths for checks. +type ExposeConfig struct { + // Checks defines whether paths associated with Consul checks will be exposed. + // This flag triggers exposing all HTTP and GRPC check paths registered for the service. + Checks bool `json:"checks,omitempty"` + + // Paths is the list of paths exposed through the proxy. + Paths []ExposePath `json:"paths,omitempty"` +} + +type ExposePath struct { + // ListenerPort defines the port of the proxy's listener for exposed paths. + ListenerPort int `json:"listenerPort,omitempty"` + + // Path is the path to expose through the proxy, ie. "/metrics." + Path string `json:"path,omitempty"` + + // LocalPathPort is the port that the service is listening on for the given path. + LocalPathPort int `json:"localPathPort,omitempty"` + + // Protocol describes the upstream's service protocol. + // Valid values are "http" and "http2", defaults to "http" + Protocol string `json:"protocol,omitempty"` +} + +//Matches returns true if the expose config of the entry is the same as the struct +func (e ExposeConfig) Matches(expose capi.ExposeConfig) bool { + if e.Checks != expose.Checks { + return false + } + + if len(e.Paths) != len(expose.Paths) { + return false + } + + for _, path := range e.Paths { + found := false + for _, entryPath := range expose.Paths { + if path.Protocol == entryPath.Protocol && + path.Path == entryPath.Path && + path.ListenerPort == entryPath.ListenerPort && + path.LocalPathPort == entryPath.LocalPathPort { + found = true + break + } + } + + if !found { + return false + } + } + return true +} + +//ToConsul returns the ExposeConfig for the entry +func (e ExposeConfig) ToConsul() capi.ExposeConfig { + return capi.ExposeConfig{ + Checks: e.Checks, + Paths: e.parseExposePath(), + } +} + +func (e ExposeConfig) parseExposePath() []capi.ExposePath { + var paths []capi.ExposePath + for _, path := range e.Paths { + paths = append(paths, capi.ExposePath{ + ListenerPort: path.ListenerPort, + Path: path.Path, + LocalPathPort: path.LocalPathPort, + Protocol: path.Protocol, + }) + } + return paths +} diff --git a/api/v1alpha1/servicedefaults_webhook_test.go b/api/v1alpha1/servicedefaults_webhook_test.go index 80431ca398..f14c68ae67 100644 --- a/api/v1alpha1/servicedefaults_webhook_test.go +++ b/api/v1alpha1/servicedefaults_webhook_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "testing" + logrtest "github.com/go-logr/logr/testing" capi "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil" "github.com/stretchr/testify/require" @@ -44,6 +45,7 @@ func TestRun_HandleErrorsIfServiceDefaultsWithSameNameExists(t *testing.T) { validator := &serviceDefaultsValidator{ Client: client, ConsulClient: consulClient, + Logger: logrtest.NullLogger{}, } decoder, err := admission.NewDecoder(scheme.Scheme) diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index 6581c883e5..3042eb8c53 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -53,79 +53,3 @@ func (m MeshGatewayConfig) ToConsul() capi.MeshGatewayConfig { } } } - -// ExposeConfig describes HTTP paths to expose through Envoy outside of Connect. -// Users can expose individual paths and/or all HTTP/GRPC paths for checks. -type ExposeConfig struct { - // Checks defines whether paths associated with Consul checks will be exposed. - // This flag triggers exposing all HTTP and GRPC check paths registered for the service. - Checks bool `json:"checks,omitempty"` - - // Paths is the list of paths exposed through the proxy. - Paths []ExposePath `json:"paths,omitempty"` -} - -type ExposePath struct { - // ListenerPort defines the port of the proxy's listener for exposed paths. - ListenerPort int `json:"listenerPort,omitempty"` - - // Path is the path to expose through the proxy, ie. "/metrics." - Path string `json:"path,omitempty"` - - // LocalPathPort is the port that the service is listening on for the given path. - LocalPathPort int `json:"localPathPort,omitempty"` - - // Protocol describes the upstream's service protocol. - // Valid values are "http" and "http2", defaults to "http" - Protocol string `json:"protocol,omitempty"` -} - -//Matches returns true if the expose config of the entry is the same as the struct -func (e ExposeConfig) Matches(expose capi.ExposeConfig) bool { - if e.Checks != expose.Checks { - return false - } - - if len(e.Paths) != len(expose.Paths) { - return false - } - - for _, path := range e.Paths { - found := false - for _, entryPath := range expose.Paths { - if path.Protocol == entryPath.Protocol && - path.Path == entryPath.Path && - path.ListenerPort == entryPath.ListenerPort && - path.LocalPathPort == entryPath.LocalPathPort { - found = true - break - } - } - - if !found { - return false - } - } - return true -} - -//ToConsul returns the ExposeConfig for the entry -func (e ExposeConfig) ToConsul() capi.ExposeConfig { - return capi.ExposeConfig{ - Checks: e.Checks, - Paths: e.parseExposePath(), - } -} - -func (e ExposeConfig) parseExposePath() []capi.ExposePath { - var paths []capi.ExposePath - for _, path := range e.Paths { - paths = append(paths, capi.ExposePath{ - ListenerPort: path.ListenerPort, - Path: path.Path, - LocalPathPort: path.LocalPathPort, - Protocol: path.Protocol, - }) - } - return paths -} diff --git a/build-support/controller/README.md b/build-support/controller/README.md new file mode 100644 index 0000000000..ba2e21ffc0 --- /dev/null +++ b/build-support/controller/README.md @@ -0,0 +1,9 @@ +# Consul-K8s hack GuideLines + +This document describes how you can use the scripts from [`hack`](.) directory +and gives a brief introduction and explanation of these scripts. + +## Overview + +The [`hack`](.) directory contains many scripts that ensure continuous development of consul-k8s, +enhance the robustness of the code, improve development efficiency, etc. diff --git a/hack/boilerplate.go.txt b/build-support/controller/boilerplate.go.txt similarity index 100% rename from hack/boilerplate.go.txt rename to build-support/controller/boilerplate.go.txt From 5f64db48289d1545a2db24317d64cea900e0ca77 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 27 Aug 2020 10:05:29 -0400 Subject: [PATCH 18/20] update PR with review comments --- api/v1alpha1/servicedefaults_types.go | 26 +- api/v1alpha1/servicedefaults_types_test.go | 637 +++++++-------------- api/v1alpha1/types.go | 19 +- build-support/controller/README.md | 10 +- commands.go | 2 +- subcommand/controller/command.go | 6 +- 6 files changed, 230 insertions(+), 470 deletions(-) diff --git a/api/v1alpha1/servicedefaults_types.go b/api/v1alpha1/servicedefaults_types.go index 3c4286213a..b243d393f6 100644 --- a/api/v1alpha1/servicedefaults_types.go +++ b/api/v1alpha1/servicedefaults_types.go @@ -51,8 +51,8 @@ func (s *ServiceDefaults) ToConsul() *capi.ServiceConfigEntry { Name: s.Name, //Namespace: s.Namespace, // todo: don't set this unless enterprise Protocol: s.Spec.Protocol, - MeshGateway: s.Spec.MeshGateway.ToConsul(), - Expose: s.Spec.Expose.ToConsul(), + MeshGateway: s.Spec.MeshGateway.toConsul(), + Expose: s.Spec.Expose.toConsul(), ExternalSNI: s.Spec.ExternalSNI, } } @@ -62,7 +62,7 @@ func (s *ServiceDefaults) MatchesConsul(entry *capi.ServiceConfigEntry) bool { return s.Name == entry.GetName() && s.Spec.Protocol == entry.Protocol && s.Spec.MeshGateway.Mode == string(entry.MeshGateway.Mode) && - s.Spec.Expose.Matches(entry.Expose) && + s.Spec.Expose.matches(entry.Expose) && s.Spec.ExternalSNI == entry.ExternalSNI } @@ -92,8 +92,8 @@ type ExposePath struct { Protocol string `json:"protocol,omitempty"` } -//Matches returns true if the expose config of the entry is the same as the struct -func (e ExposeConfig) Matches(expose capi.ExposeConfig) bool { +// matches returns true if the expose config of the entry is the same as the struct +func (e ExposeConfig) matches(expose capi.ExposeConfig) bool { if e.Checks != expose.Checks { return false } @@ -121,15 +121,8 @@ func (e ExposeConfig) Matches(expose capi.ExposeConfig) bool { return true } -//ToConsul returns the ExposeConfig for the entry -func (e ExposeConfig) ToConsul() capi.ExposeConfig { - return capi.ExposeConfig{ - Checks: e.Checks, - Paths: e.parseExposePath(), - } -} - -func (e ExposeConfig) parseExposePath() []capi.ExposePath { +// toConsul returns the ExposeConfig for the entry +func (e ExposeConfig) toConsul() capi.ExposeConfig { var paths []capi.ExposePath for _, path := range e.Paths { paths = append(paths, capi.ExposePath{ @@ -139,5 +132,8 @@ func (e ExposeConfig) parseExposePath() []capi.ExposePath { Protocol: path.Protocol, }) } - return paths + return capi.ExposeConfig{ + Checks: e.Checks, + Paths: paths, + } } diff --git a/api/v1alpha1/servicedefaults_types_test.go b/api/v1alpha1/servicedefaults_types_test.go index 3833e7dbce..c2968e0e61 100644 --- a/api/v1alpha1/servicedefaults_types_test.go +++ b/api/v1alpha1/servicedefaults_types_test.go @@ -8,94 +8,207 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func Test_ToConsul(t *testing.T) { +func TestToConsul(t *testing.T) { cases := map[string]struct { input *ServiceDefaults expected *capi.ServiceConfigEntry }{ - "protocol:http,mode:remote": { + "kind:service-defaults": { + &ServiceDefaults{}, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + }, + }, + "name:resource-name": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", + Name: "resource-name", }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "resource-name", + }, + }, + "protocol:http": { + &ServiceDefaults{ Spec: ServiceDefaultsSpec{ Protocol: "http", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Protocol: "http", + }, + }, + "protocol:https": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + Protocol: "https", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Protocol: "https", + }, + }, + "protocol:''": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + Protocol: "", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Protocol: "", + }, + }, + "mode:unsupported": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ MeshGateway: MeshGatewayConfig{ - Mode: "remote", + Mode: "unsupported", }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", + Kind: capi.ServiceDefaults, MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeRemote, + Mode: capi.MeshGatewayModeDefault, }, }, }, - "protocol:https,mode:local,exposePaths:1,externalSNI": { + "mode:local": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", MeshGateway: MeshGatewayConfig{ Mode: "local", }, - Expose: ExposeConfig{ - Checks: true, - Paths: []ExposePath{ - { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - }, - }, - }, - ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", + Kind: capi.ServiceDefaults, MeshGateway: capi.MeshGatewayConfig{ Mode: capi.MeshGatewayModeLocal, }, - Expose: capi.ExposeConfig{ - Checks: true, - Paths: []capi.ExposePath{ - { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - }, + }, + }, + "mode:remote": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + MeshGateway: MeshGatewayConfig{ + Mode: "remote", }, }, - ExternalSNI: "test-external-sni", + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, }, }, - "protocol:\"\",mode:\"\",exposePaths:2,externalSNI": { + "mode:none": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", + Spec: ServiceDefaultsSpec{ + MeshGateway: MeshGatewayConfig{ + Mode: "none", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeNone, }, + }, + }, + "mode:default": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + MeshGateway: MeshGatewayConfig{ + Mode: "default", + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeDefault, + }, + }, + }, + "mode:''": { + &ServiceDefaults{ Spec: ServiceDefaultsSpec{ - Protocol: "", MeshGateway: MeshGatewayConfig{ Mode: "", }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeDefault, + }, + }, + }, + "externalSNI:test-external-sni": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + ExternalSNI: "test-external-sni", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + ExternalSNI: "test-external-sni", + }, + }, + "externalSNI:''": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + ExternalSNI: "", + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + ExternalSNI: "", + }, + }, + "expose.checks:false": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + Expose: ExposeConfig{ + Checks: false, + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Expose: capi.ExposeConfig{ + Checks: false, + }, + }, + }, + "expose.checks:true": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ Expose: ExposeConfig{ Checks: true, + }, + }, + }, + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Expose: capi.ExposeConfig{ + Checks: true, + }, + }, + }, + "expose.paths:single": { + &ServiceDefaults{ + Spec: ServiceDefaultsSpec{ + Expose: ExposeConfig{ Paths: []ExposePath{ { ListenerPort: 80, @@ -103,27 +216,13 @@ func Test_ToConsul(t *testing.T) { LocalPathPort: 42, Protocol: "tcp", }, - { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - }, }, }, - ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeDefault, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { ListenerPort: 80, @@ -131,30 +230,14 @@ func Test_ToConsul(t *testing.T) { LocalPathPort: 42, Protocol: "tcp", }, - { - ListenerPort: 8080, - Path: "/second/test/path", - LocalPathPort: 11, - Protocol: "https", - }, }, }, - ExternalSNI: "test-external-sni", }, }, - "protocol:http,mode:none,exposePaths:1,exposeChecks:false": { + "expose.paths:multiple": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "http", - MeshGateway: MeshGatewayConfig{ - Mode: "none", - }, Expose: ExposeConfig{ - Checks: false, Paths: []ExposePath{ { ListenerPort: 80, @@ -162,20 +245,19 @@ func Test_ToConsul(t *testing.T) { LocalPathPort: 42, Protocol: "tcp", }, + { + ListenerPort: 8080, + Path: "/root/test/path", + LocalPathPort: 4201, + Protocol: "https", + }, }, }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeNone, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: false, Paths: []capi.ExposePath{ { ListenerPort: 80, @@ -183,33 +265,16 @@ func Test_ToConsul(t *testing.T) { LocalPathPort: 42, Protocol: "tcp", }, + { + ListenerPort: 8080, + Path: "/root/test/path", + LocalPathPort: 4201, + Protocol: "https", + }, }, }, }, }, - "protocol:https,mode:unsupported": { - &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, - Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "unsupported", - }, - }, - }, - &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeDefault, - }, - }, - }, } for name, testCase := range cases { @@ -220,7 +285,7 @@ func Test_ToConsul(t *testing.T) { } } -func Test_MatchesConsul(t *testing.T) { +func TestMatchesConsul(t *testing.T) { cases := map[string]struct { internal *ServiceDefaults consul *capi.ServiceConfigEntry @@ -229,111 +294,63 @@ func Test_MatchesConsul(t *testing.T) { "name:matches": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, - Spec: ServiceDefaultsSpec{ - Protocol: "http", - MeshGateway: MeshGatewayConfig{ - Mode: "remote", - }, + Name: "my-test-service", }, + Spec: ServiceDefaultsSpec{}, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeRemote, - }, + Kind: capi.ServiceDefaults, + Name: "my-test-service", }, true, }, "name:mismatched": { &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, - Spec: ServiceDefaultsSpec{ - Protocol: "http", - MeshGateway: MeshGatewayConfig{ - Mode: "remote", - }, + Name: "my-test-service", }, + Spec: ServiceDefaultsSpec{}, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "differently-named-service", - Namespace: "", - Protocol: "http", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeRemote, - }, + Kind: capi.ServiceDefaults, + Name: "differently-named-service", }, false, }, "protocol:matches": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ Protocol: "http", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeDefault, - }, + Kind: capi.ServiceDefaults, + Protocol: "http", }, true, }, "protocol:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ Protocol: "http", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeDefault, - }, + Kind: capi.ServiceDefaults, + Protocol: "https", }, false, }, "gatewayConfig:matches": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ - Protocol: "http", MeshGateway: MeshGatewayConfig{ Mode: "remote", }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", + Kind: capi.ServiceDefaults, MeshGateway: capi.MeshGatewayConfig{ Mode: capi.MeshGatewayModeRemote, }, @@ -342,22 +359,14 @@ func Test_MatchesConsul(t *testing.T) { }, "gatewayConfig:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ - Protocol: "http", MeshGateway: MeshGatewayConfig{ Mode: "remote", }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", + Kind: capi.ServiceDefaults, MeshGateway: capi.MeshGatewayConfig{ Mode: capi.MeshGatewayModeLocal, }, @@ -366,165 +375,64 @@ func Test_MatchesConsul(t *testing.T) { }, "externalSNI:matches": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ - Protocol: "http", - MeshGateway: MeshGatewayConfig{ - Mode: "remote", - }, ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeRemote, - }, + Kind: capi.ServiceDefaults, ExternalSNI: "test-external-sni", }, true, }, "externalSNI:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ - Protocol: "http", - MeshGateway: MeshGatewayConfig{ - Mode: "remote", - }, ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "http", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, ExternalSNI: "different-external-sni", }, false, }, "expose.checks:matches": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ Checks: true, - Paths: []ExposePath{ - { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - }, - }, }, - ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ Checks: true, - Paths: []capi.ExposePath{ - { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - }, - }, }, - ExternalSNI: "test-external-sni", }, true, }, "expose.checks:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-config", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ Checks: true, - Paths: []ExposePath{ - { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - }, - }, }, - ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ Checks: false, - Paths: []capi.ExposePath{ - { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", - }, - }, }, - ExternalSNI: "test-external-sni", }, false, }, "expose.paths:matches": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ - Checks: true, Paths: []ExposePath{ { ListenerPort: 80, @@ -543,15 +451,8 @@ func Test_MatchesConsul(t *testing.T) { }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { ListenerPort: 80, @@ -572,44 +473,22 @@ func Test_MatchesConsul(t *testing.T) { }, "expose.paths.listenerPort:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ - Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", + ListenerPort: 80, }, }, }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 81, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", + ListenerPort: 81, }, }, }, @@ -618,44 +497,22 @@ func Test_MatchesConsul(t *testing.T) { }, "expose.paths.path:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ - Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", + Path: "/test/path", }, }, }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/differnt/path", - LocalPathPort: 42, - Protocol: "tcp", + Path: "/differnt/path", }, }, }, @@ -664,44 +521,22 @@ func Test_MatchesConsul(t *testing.T) { }, "expose.paths.localPathPort:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ - Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", LocalPathPort: 42, - Protocol: "tcp", }, }, }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/test/path", LocalPathPort: 21, - Protocol: "tcp", }, }, }, @@ -710,44 +545,22 @@ func Test_MatchesConsul(t *testing.T) { }, "expose.paths.protocol:mismatched": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ - Checks: true, Paths: []ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "tcp", + Protocol: "tcp", }, }, }, }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { - ListenerPort: 80, - Path: "/test/path", - LocalPathPort: 42, - Protocol: "https", + Protocol: "https", }, }, }, @@ -756,17 +569,8 @@ func Test_MatchesConsul(t *testing.T) { }, "expose.paths:mismatched when path lengths are different": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ - Checks: true, Paths: []ExposePath{ { ListenerPort: 8080, @@ -782,19 +586,11 @@ func Test_MatchesConsul(t *testing.T) { }, }, }, - ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { ListenerPort: 8080, @@ -804,23 +600,13 @@ func Test_MatchesConsul(t *testing.T) { }, }, }, - ExternalSNI: "test-external-sni", }, false, }, "expose.paths:match when paths orders are different": { &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - Namespace: "consul-configs", - }, Spec: ServiceDefaultsSpec{ - Protocol: "https", - MeshGateway: MeshGatewayConfig{ - Mode: "local", - }, Expose: ExposeConfig{ - Checks: true, Paths: []ExposePath{ { ListenerPort: 8080, @@ -836,19 +622,11 @@ func Test_MatchesConsul(t *testing.T) { }, }, }, - ExternalSNI: "test-external-sni", }, }, &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "my-test-service", - Namespace: "", - Protocol: "https", - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - }, + Kind: capi.ServiceDefaults, Expose: capi.ExposeConfig{ - Checks: true, Paths: []capi.ExposePath{ { ListenerPort: 80, @@ -864,7 +642,6 @@ func Test_MatchesConsul(t *testing.T) { }, }, }, - ExternalSNI: "test-external-sni", }, true, }, diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index 3042eb8c53..dedad19908 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -32,20 +32,13 @@ type MeshGatewayConfig struct { Mode string `json:"mode,omitempty"` } -//ToConsul returns the MeshGatewayConfig for the entry -func (m MeshGatewayConfig) ToConsul() capi.MeshGatewayConfig { - switch m.Mode { - case string(capi.MeshGatewayModeLocal): +// toConsul returns the MeshGatewayConfig for the entry +func (m MeshGatewayConfig) toConsul() capi.MeshGatewayConfig { + mode := capi.MeshGatewayMode(m.Mode) + switch mode { + case capi.MeshGatewayModeLocal, capi.MeshGatewayModeRemote, capi.MeshGatewayModeNone: return capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeLocal, - } - case string(capi.MeshGatewayModeNone): - return capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeNone, - } - case string(capi.MeshGatewayModeRemote): - return capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeRemote, + Mode: mode, } default: return capi.MeshGatewayConfig{ diff --git a/build-support/controller/README.md b/build-support/controller/README.md index ba2e21ffc0..0d24937531 100644 --- a/build-support/controller/README.md +++ b/build-support/controller/README.md @@ -1,9 +1,5 @@ -# Consul-K8s hack GuideLines - -This document describes how you can use the scripts from [`hack`](.) directory -and gives a brief introduction and explanation of these scripts. - ## Overview -The [`hack`](.) directory contains many scripts that ensure continuous development of consul-k8s, -enhance the robustness of the code, improve development efficiency, etc. +`boilerplate.go.txt` is a file required by `operator-sdk` when it performs code-generation. + +It's contents provide the headers to the generated files but as we do not require headers for the files we generate, it has been left intentionally blank. diff --git a/commands.go b/commands.go index 8317e3e9e4..c01d9cc138 100644 --- a/commands.go +++ b/commands.go @@ -66,7 +66,7 @@ func init() { }, "controller": func() (cli.Command, error) { - return &cmdController.Command{UI: ui}, nil + return &cmdController.Command{}, nil }, } } diff --git a/subcommand/controller/command.go b/subcommand/controller/command.go index 3031927f63..0c8cda8c00 100644 --- a/subcommand/controller/command.go +++ b/subcommand/controller/command.go @@ -1,15 +1,14 @@ package controller import ( + "errors" "flag" - "fmt" "os" "sync" "github.com/hashicorp/consul-k8s/api/v1alpha1" "github.com/hashicorp/consul-k8s/controllers" "github.com/hashicorp/consul-k8s/subcommand/flags" - "github.com/mitchellh/cli" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -19,7 +18,6 @@ import ( ) type Command struct { - UI cli.Ui flagSet *flag.FlagSet k8s *flags.K8SFlags httpFlags *flags.HTTPFlags @@ -60,7 +58,7 @@ func (c *Command) Run(_ []string) int { return 1 } if len(c.flagSet.Args()) > 0 { - c.UI.Error(fmt.Sprintf("Should have no non-flag arguments.")) + setupLog.Error(errors.New("should have no non-flag arguments"), "invalid arguments") return 1 } From 40ec5edadf0b9f0d5aa0172ff39890a9faee01fc Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 27 Aug 2020 15:35:13 -0400 Subject: [PATCH 19/20] Update webhook name --- api/v1alpha1/servicedefaults_webhook.go | 2 +- config/webhook/manifests.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v1alpha1/servicedefaults_webhook.go b/api/v1alpha1/servicedefaults_webhook.go index 97310cad28..5ecb9c0b8a 100644 --- a/api/v1alpha1/servicedefaults_webhook.go +++ b/api/v1alpha1/servicedefaults_webhook.go @@ -28,7 +28,7 @@ type serviceDefaultsValidator struct { } // Note: The path value in the below line is the path to the webhook. If it is updates, run code-gen, update subcommand/controller/command.go and the consul-helm value for the path to the webhook. -// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=mservicedefaults.consul.io +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=mutate-servicedefaults.consul.hashicorp.com func (v *serviceDefaultsValidator) Handle(ctx context.Context, req admission.Request) admission.Response { var svcDefaults ServiceDefaults diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 9e62469bb2..b4139c5eb9 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -13,7 +13,7 @@ webhooks: namespace: system path: /mutate-v1alpha1-servicedefaults failurePolicy: Fail - name: mservicedefaults.consul.io + name: mutate-servicedefaults.consul.hashicorp.com rules: - apiGroups: - consul.hashicorp.com From cba4b5d5725f2f03b39b2043f71cd3a1f7414804 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 27 Aug 2020 16:54:31 -0400 Subject: [PATCH 20/20] update PR based on review comments --- api/v1alpha1/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index dedad19908..30f09be4b5 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -16,8 +16,8 @@ const ( // should be direct and not flow through a mesh gateway. MeshGatewayModeNone MeshGatewayMode = "none" - // MeshGatewayModeLocal represents that the Upstrea Connect connections - // should be made to a mesh gateway in the local datacenter. This is + // MeshGatewayModeLocal represents that the Upstream Connect connections + // should be made to a mesh gateway in the local datacenter. MeshGatewayModeLocal MeshGatewayMode = "local" // MeshGatewayModeRemote represents that the Upstream Connect connections