From c731f0360db201e52e3724836eb5395a60e917ae Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Tue, 26 Oct 2021 09:54:05 -0400 Subject: [PATCH] Rename service-exports -> partition-exports --- .../consul/templates/crd-serviceexports.yaml | 137 -------- .../consul/test/unit/crd-serviceexports.bats | 24 -- .../api/v1alpha1/serviceexports_types.go | 185 ---------- .../api/v1alpha1/serviceexports_types_test.go | 332 ------------------ .../api/v1alpha1/serviceexports_webhook.go | 70 ---- .../v1alpha1/serviceexports_webhook_test.go | 105 ------ .../api/v1alpha1/zz_generated.deepcopy.go | 81 ----- .../consul.hashicorp.com_serviceexports.yaml | 130 ------- .../cainjection_in_serviceexports.yaml | 8 - .../patches/webhook_in_serviceexports.yaml | 17 - .../rbac/serviceexport_editor_role.yaml | 24 -- .../rbac/serviceexport_viewer_role.yaml | 20 -- .../consul_v1alpha1_serviceexport.yaml | 7 - .../controller/serviceexports_controller.go | 40 --- 14 files changed, 1180 deletions(-) delete mode 100644 charts/consul/templates/crd-serviceexports.yaml delete mode 100644 charts/consul/test/unit/crd-serviceexports.bats delete mode 100644 control-plane/api/v1alpha1/serviceexports_types.go delete mode 100644 control-plane/api/v1alpha1/serviceexports_types_test.go delete mode 100644 control-plane/api/v1alpha1/serviceexports_webhook.go delete mode 100644 control-plane/api/v1alpha1/serviceexports_webhook_test.go delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_serviceexports.yaml delete mode 100644 control-plane/config/crd/patches/cainjection_in_serviceexports.yaml delete mode 100644 control-plane/config/crd/patches/webhook_in_serviceexports.yaml delete mode 100644 control-plane/config/rbac/serviceexport_editor_role.yaml delete mode 100644 control-plane/config/rbac/serviceexport_viewer_role.yaml delete mode 100644 control-plane/config/samples/consul_v1alpha1_serviceexport.yaml delete mode 100644 control-plane/controller/serviceexports_controller.go diff --git a/charts/consul/templates/crd-serviceexports.yaml b/charts/consul/templates/crd-serviceexports.yaml deleted file mode 100644 index 2f6c3f3cc3..0000000000 --- a/charts/consul/templates/crd-serviceexports.yaml +++ /dev/null @@ -1,137 +0,0 @@ -{{- if .Values.controller.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.0 - creationTimestamp: null - name: serviceexports.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: ServiceExports - listKind: ServiceExportsList - plural: serviceexports - singular: serviceexports - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: ServiceExports is the Schema for the serviceexports 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: ServiceExportsSpec defines the desired state of ServiceExports - properties: - services: - description: Services is a list of services to be exported and the - list of partitions to expose them to. - items: - description: ExportedService manages the exporting of a service - in the local partition to other partitions. - properties: - consumers: - description: Consumers is a list of downstream consumers of - the service to be exported. - items: - description: ServiceConsumer represents a downstream consumer - of the service to be exported. - properties: - partition: - description: Partition is the admin partition to export - the service to. - type: string - type: object - type: array - name: - description: Name is the name of the service to be exported. - type: string - namespace: - description: Namespace is the namespace to export the service - from. - type: string - type: object - type: array - type: object - status: - 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 - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/test/unit/crd-serviceexports.bats b/charts/consul/test/unit/crd-serviceexports.bats deleted file mode 100644 index 21496173d7..0000000000 --- a/charts/consul/test/unit/crd-serviceexports.bats +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "serviceExports/CustomerResourceDefinition: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-serviceexports.yaml \ - . -} - -@test "serviceExports/CustomerResourceDefinition: enabled with controller.enabled=true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-serviceexports.yaml \ - --set 'controller.enabled=true' \ - . | tee /dev/stderr | - # The generated CRDs have "---" at the top which results in two objects - # being detected by yq, the first of which is null. We must therefore use - # yq -s so that length operates on both objects at once rather than - # individually, which would output false\ntrue and fail the test. - yq -s 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} diff --git a/control-plane/api/v1alpha1/serviceexports_types.go b/control-plane/api/v1alpha1/serviceexports_types.go deleted file mode 100644 index 75c00b9ebe..0000000000 --- a/control-plane/api/v1alpha1/serviceexports_types.go +++ /dev/null @@ -1,185 +0,0 @@ -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - "github.com/hashicorp/consul/api" - capi "github.com/hashicorp/consul/api" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ServiceExportsKubeKind = "serviceexports" - -func init() { - SchemeBuilder.Register(&ServiceExports{}, &ServiceExportsList{}) -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// ServiceExports is the Schema for the serviceexports API -// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" -// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" -// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" -type ServiceExports struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ServiceExportsSpec `json:"spec,omitempty"` - Status `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// ServiceExportsList contains a list of ServiceExports -type ServiceExportsList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []ServiceExports `json:"items"` -} - -// ServiceExportsSpec defines the desired state of ServiceExports -type ServiceExportsSpec struct { - // Services is a list of services to be exported and the list of partitions - // to expose them to. - Services []ExportedService `json:"services,omitempty"` -} - -// ExportedService manages the exporting of a service in the local partition to -// other partitions. -type ExportedService struct { - // Name is the name of the service to be exported. - Name string `json:"name,omitempty"` - - // Namespace is the namespace to export the service from. - Namespace string `json:"namespace,omitempty"` - - // Consumers is a list of downstream consumers of the service to be exported. - Consumers []ServiceConsumer `json:"consumers,omitempty"` -} - -// ServiceConsumer represents a downstream consumer of the service to be exported. -type ServiceConsumer struct { - // Partition is the admin partition to export the service to. - Partition string `json:"partition,omitempty"` -} - -func (in *ServiceExports) GetObjectMeta() metav1.ObjectMeta { - return in.ObjectMeta -} - -func (in *ServiceExports) AddFinalizer(name string) { - in.ObjectMeta.Finalizers = append(in.Finalizers(), name) -} - -func (in *ServiceExports) RemoveFinalizer(name string) { - var newFinalizers []string - for _, oldF := range in.Finalizers() { - if oldF != name { - newFinalizers = append(newFinalizers, oldF) - } - } - in.ObjectMeta.Finalizers = newFinalizers -} - -func (in *ServiceExports) Finalizers() []string { - return in.ObjectMeta.Finalizers -} - -func (in *ServiceExports) ConsulKind() string { - return capi.ServiceExports -} - -func (in *ServiceExports) ConsulGlobalResource() bool { - return true -} - -func (in *ServiceExports) ConsulMirroringNS() string { - return common.DefaultConsulNamespace -} - -func (in *ServiceExports) KubeKind() string { - return ServiceExportsKubeKind -} - -func (in *ServiceExports) ConsulName() string { - return in.ObjectMeta.Name -} - -func (in *ServiceExports) KubernetesName() string { - return in.ObjectMeta.Name -} - -func (in *ServiceExports) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { - in.Status.Conditions = Conditions{ - { - Type: ConditionSynced, - Status: status, - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: message, - }, - } -} - -func (in *ServiceExports) SetLastSyncedTime(time *metav1.Time) { - in.Status.LastSyncedTime = time -} - -func (in *ServiceExports) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { - cond := in.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown, "", "" - } - return cond.Status, cond.Reason, cond.Message -} - -func (in *ServiceExports) SyncedConditionStatus() corev1.ConditionStatus { - cond := in.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown - } - return cond.Status -} - -func (in *ServiceExports) ToConsul(datacenter string) api.ConfigEntry { - var services []capi.ExportedService - for _, service := range in.Spec.Services { - services = append(services, service.toConsul()) - } - return &capi.ServiceExportsConfigEntry{ - Services: services, - Meta: meta(datacenter), - } -} - -func (in *ExportedService) toConsul() capi.ExportedService { - var consumers []capi.ServiceConsumer - for _, consumer := range in.Consumers { - consumers = append(consumers, capi.ServiceConsumer{Partition: consumer.Partition}) - } - return capi.ExportedService{ - Name: in.Name, - Namespace: in.Namespace, - Consumers: consumers, - } -} - -func (in *ServiceExports) MatchesConsul(candidate api.ConfigEntry) bool { - configEntry, ok := candidate.(*capi.ServiceExportsConfigEntry) - if !ok { - return false - } - // No datacenter is passed to ToConsul as we ignore the Meta field when checking for equality. - return cmp.Equal(in.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.ServiceExportsConfigEntry{}, "Partition", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty()) - -} - -func (in *ServiceExports) Validate(_ bool) error { - return nil -} - -func (in *ServiceExports) DefaultNamespaceFields(_ bool, _ string, _ bool, _ string) { -} diff --git a/control-plane/api/v1alpha1/serviceexports_types_test.go b/control-plane/api/v1alpha1/serviceexports_types_test.go deleted file mode 100644 index 97cc0bd6bd..0000000000 --- a/control-plane/api/v1alpha1/serviceexports_types_test.go +++ /dev/null @@ -1,332 +0,0 @@ -package v1alpha1 - -import ( - "testing" - "time" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" - capi "github.com/hashicorp/consul/api" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Test MatchesConsul for cases that should return true. -func TestServiceExports_MatchesConsul(t *testing.T) { - cases := map[string]struct { - Ours ServiceExports - Theirs capi.ConfigEntry - Matches bool - }{ - "empty fields matches": { - Ours: ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - Spec: ServiceExportsSpec{}, - }, - Theirs: &capi.ServiceExportsConfigEntry{ - CreateIndex: 1, - ModifyIndex: 2, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - Matches: true, - }, - "all fields set matches": { - Ours: ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - Spec: ServiceExportsSpec{ - Services: []ExportedService{ - { - Name: "service-frontend", - Namespace: "frontend", - Consumers: []ServiceConsumer{ - { - Partition: "second", - }, - { - Partition: "third", - }, - }, - }, - { - Name: "service-backend", - Namespace: "backend", - Consumers: []ServiceConsumer{ - { - Partition: "fourth", - }, - { - Partition: "fifth", - }, - }, - }, - }, - }, - }, - Theirs: &capi.ServiceExportsConfigEntry{ - Partition: "default", - Services: []capi.ExportedService{ - { - Name: "service-frontend", - Namespace: "frontend", - Consumers: []capi.ServiceConsumer{ - { - Partition: "second", - }, - { - Partition: "third", - }, - }, - }, - { - Name: "service-backend", - Namespace: "backend", - Consumers: []capi.ServiceConsumer{ - { - Partition: "fourth", - }, - { - Partition: "fifth", - }, - }, - }, - }, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - CreateIndex: 1, - ModifyIndex: 2, - }, - Matches: true, - }, - "mismatched types does not match": { - Ours: ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - Spec: ServiceExportsSpec{}, - }, - Theirs: &capi.ServiceConfigEntry{ - Name: common.Exports, - Kind: capi.ServiceExports, - }, - Matches: false, - }, - } - for name, c := range cases { - t.Run(name, func(t *testing.T) { - require.Equal(t, c.Matches, c.Ours.MatchesConsul(c.Theirs)) - }) - } -} - -func TestServiceExports_ToConsul(t *testing.T) { - cases := map[string]struct { - Ours ServiceExports - Exp *capi.ServiceExportsConfigEntry - }{ - "empty fields": { - Ours: ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - Spec: ServiceExportsSpec{}, - }, - Exp: &capi.ServiceExportsConfigEntry{ - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - }, - "every field set": { - Ours: ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - Spec: ServiceExportsSpec{ - Services: []ExportedService{ - { - Name: "service-frontend", - Namespace: "frontend", - Consumers: []ServiceConsumer{ - { - Partition: "second", - }, - { - Partition: "third", - }, - }, - }, - { - Name: "service-backend", - Namespace: "backend", - Consumers: []ServiceConsumer{ - { - Partition: "fourth", - }, - { - Partition: "fifth", - }, - }, - }, - }, - }, - }, - Exp: &capi.ServiceExportsConfigEntry{ - Services: []capi.ExportedService{ - { - Name: "service-frontend", - Namespace: "frontend", - Consumers: []capi.ServiceConsumer{ - { - Partition: "second", - }, - { - Partition: "third", - }, - }, - }, - { - Name: "service-backend", - Namespace: "backend", - Consumers: []capi.ServiceConsumer{ - { - Partition: "fourth", - }, - { - Partition: "fifth", - }, - }, - }, - }, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - }, - } - for name, c := range cases { - t.Run(name, func(t *testing.T) { - act := c.Ours.ToConsul("datacenter") - serviceExports, ok := act.(*capi.ServiceExportsConfigEntry) - require.True(t, ok, "could not cast") - require.Equal(t, c.Exp, serviceExports) - }) - } -} - -func TestServiceExports_AddFinalizer(t *testing.T) { - serviceExports := &ServiceExports{} - serviceExports.AddFinalizer("finalizer") - require.Equal(t, []string{"finalizer"}, serviceExports.ObjectMeta.Finalizers) -} - -func TestServiceExports_RemoveFinalizer(t *testing.T) { - serviceExports := &ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{"f1", "f2"}, - }, - } - serviceExports.RemoveFinalizer("f1") - require.Equal(t, []string{"f2"}, serviceExports.ObjectMeta.Finalizers) -} - -func TestServiceExports_SetSyncedCondition(t *testing.T) { - serviceExports := &ServiceExports{} - serviceExports.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") - - require.Equal(t, corev1.ConditionTrue, serviceExports.Status.Conditions[0].Status) - require.Equal(t, "reason", serviceExports.Status.Conditions[0].Reason) - require.Equal(t, "message", serviceExports.Status.Conditions[0].Message) - now := metav1.Now() - require.True(t, serviceExports.Status.Conditions[0].LastTransitionTime.Before(&now)) -} - -func TestServiceExports_SetLastSyncedTime(t *testing.T) { - serviceExports := &ServiceExports{} - syncedTime := metav1.NewTime(time.Now()) - serviceExports.SetLastSyncedTime(&syncedTime) - - require.Equal(t, &syncedTime, serviceExports.Status.LastSyncedTime) -} - -func TestServiceExports_GetSyncedConditionStatus(t *testing.T) { - cases := []corev1.ConditionStatus{ - corev1.ConditionUnknown, - corev1.ConditionFalse, - corev1.ConditionTrue, - } - for _, status := range cases { - t.Run(string(status), func(t *testing.T) { - serviceExports := &ServiceExports{ - Status: Status{ - Conditions: []Condition{{ - Type: ConditionSynced, - Status: status, - }}, - }, - } - - require.Equal(t, status, serviceExports.SyncedConditionStatus()) - }) - } -} - -func TestServiceExports_GetConditionWhenStatusNil(t *testing.T) { - require.Nil(t, (&ServiceExports{}).GetCondition(ConditionSynced)) -} - -func TestServiceExports_SyncedConditionStatusWhenStatusNil(t *testing.T) { - require.Equal(t, corev1.ConditionUnknown, (&ServiceExports{}).SyncedConditionStatus()) -} - -func TestServiceExports_SyncedConditionWhenStatusNil(t *testing.T) { - status, reason, message := (&ServiceExports{}).SyncedCondition() - require.Equal(t, corev1.ConditionUnknown, status) - require.Equal(t, "", reason) - require.Equal(t, "", message) -} - -func TestServiceExports_ConsulKind(t *testing.T) { - require.Equal(t, capi.ServiceExports, (&ServiceExports{}).ConsulKind()) -} - -func TestServiceExports_KubeKind(t *testing.T) { - require.Equal(t, "serviceexports", (&ServiceExports{}).KubeKind()) -} - -func TestServiceExports_ConsulName(t *testing.T) { - require.Equal(t, "foo", (&ServiceExports{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) -} - -func TestServiceExports_KubernetesName(t *testing.T) { - require.Equal(t, "foo", (&ServiceExports{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) -} - -func TestServiceExports_ConsulNamespace(t *testing.T) { - require.Equal(t, common.DefaultConsulNamespace, (&ServiceExports{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}}).ConsulMirroringNS()) -} - -func TestServiceExports_ConsulGlobalResource(t *testing.T) { - require.True(t, (&ServiceExports{}).ConsulGlobalResource()) -} - -func TestServiceExports_ObjectMeta(t *testing.T) { - meta := metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - } - ServiceExports := &ServiceExports{ - ObjectMeta: meta, - } - require.Equal(t, meta, ServiceExports.GetObjectMeta()) -} diff --git a/control-plane/api/v1alpha1/serviceexports_webhook.go b/control-plane/api/v1alpha1/serviceexports_webhook.go deleted file mode 100644 index 034ad60071..0000000000 --- a/control-plane/api/v1alpha1/serviceexports_webhook.go +++ /dev/null @@ -1,70 +0,0 @@ -package v1alpha1 - -import ( - "context" - "fmt" - "net/http" - - "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - capi "github.com/hashicorp/consul/api" - admissionv1 "k8s.io/api/admission/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// +kubebuilder:object:generate=false - -type ServiceExportsWebhook struct { - client.Client - ConsulClient *capi.Client - Logger logr.Logger - decoder *admission.Decoder - EnableConsulNamespaces bool - EnableNSMirroring bool -} - -// NOTE: The path value in the below line is the path to the webhook. -// If it is updated, run code-gen, update subcommand/controller/command.go -// and the consul-helm value for the path to the webhook. -// -// NOTE: The below line cannot be combined with any other comment. If it is -// it will break the code generation. -// -// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-service-exports,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=serviceexports,versions=v1alpha1,name=mutate-serviceexports.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 - -func (v *ServiceExportsWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { - var serviceExports ServiceExports - var serviceExportsList ServiceExportsList - err := v.decoder.Decode(req, &serviceExports) - if err != nil { - return admission.Errored(http.StatusBadRequest, err) - } - - if req.Operation == admissionv1.Create { - v.Logger.Info("validate create", "name", serviceExports.KubernetesName()) - - if serviceExports.KubernetesName() != common.Exports { - return admission.Errored(http.StatusBadRequest, - fmt.Errorf(`%s resource name must be "%s"`, - serviceExports.KubeKind(), common.Exports)) - } - - if err := v.Client.List(ctx, &serviceExportsList); err != nil { - return admission.Errored(http.StatusInternalServerError, err) - } - - if len(serviceExportsList.Items) > 0 { - return admission.Errored(http.StatusBadRequest, - fmt.Errorf("%s resource already defined - only one serviceexports entry is supported", - serviceExports.KubeKind())) - } - } - - return admission.Allowed(fmt.Sprintf("valid %s request", serviceExports.KubeKind())) -} - -func (v *ServiceExportsWebhook) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} diff --git a/control-plane/api/v1alpha1/serviceexports_webhook_test.go b/control-plane/api/v1alpha1/serviceexports_webhook_test.go deleted file mode 100644 index 43bea9fd85..0000000000 --- a/control-plane/api/v1alpha1/serviceexports_webhook_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package v1alpha1 - -import ( - "context" - "encoding/json" - "testing" - - logrtest "github.com/go-logr/logr/testing" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - "github.com/stretchr/testify/require" - admissionv1 "k8s.io/api/admission/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -func TestValidateServiceExports(t *testing.T) { - otherNS := "other" - - cases := map[string]struct { - existingResources []runtime.Object - newResource *ServiceExports - expAllow bool - expErrMessage string - }{ - "no duplicates, valid": { - existingResources: nil, - newResource: &ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - Spec: ServiceExportsSpec{}, - }, - expAllow: true, - }, - "serviceexports exists": { - existingResources: []runtime.Object{&ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - }}, - newResource: &ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Exports, - }, - Spec: ServiceExportsSpec{ - Services: []ExportedService{ - { - Name: "service", - Namespace: "service-ns", - Consumers: []ServiceConsumer{{Partition: "other"}}, - }, - }, - }, - }, - expAllow: false, - expErrMessage: "serviceexports resource already defined - only one serviceexports entry is supported", - }, - "name not exports": { - existingResources: []runtime.Object{}, - newResource: &ServiceExports{ - ObjectMeta: metav1.ObjectMeta{ - Name: "local", - }, - }, - expAllow: false, - expErrMessage: "serviceexports resource name must be \"exports\"", - }, - } - for name, c := range cases { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - marshalledRequestObject, err := json.Marshal(c.newResource) - require.NoError(t, err) - s := runtime.NewScheme() - s.AddKnownTypes(GroupVersion, &ServiceExports{}, &ServiceExportsList{}) - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.existingResources...).Build() - decoder, err := admission.NewDecoder(s) - require.NoError(t, err) - - validator := &ServiceExportsWebhook{ - Client: client, - ConsulClient: nil, - Logger: logrtest.TestLogger{T: t}, - decoder: decoder, - } - response := validator.Handle(ctx, admission.Request{ - AdmissionRequest: admissionv1.AdmissionRequest{ - Name: c.newResource.KubernetesName(), - Namespace: otherNS, - Operation: admissionv1.Create, - Object: runtime.RawExtension{ - Raw: marshalledRequestObject, - }, - }, - }) - - require.Equal(t, c.expAllow, response.Allowed) - if c.expErrMessage != "" { - require.Equal(t, c.expErrMessage, response.AdmissionResponse.Result.Message) - } - }) - } -} diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index 9492465d81..04288255f1 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -856,87 +856,6 @@ func (in *ServiceDefaultsSpec) DeepCopy() *ServiceDefaultsSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceExports) DeepCopyInto(out *ServiceExports) { - *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 ServiceExports. -func (in *ServiceExports) DeepCopy() *ServiceExports { - if in == nil { - return nil - } - out := new(ServiceExports) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceExports) 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 *ServiceExportsList) DeepCopyInto(out *ServiceExportsList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ServiceExports, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportsList. -func (in *ServiceExportsList) DeepCopy() *ServiceExportsList { - if in == nil { - return nil - } - out := new(ServiceExportsList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceExportsList) 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 *ServiceExportsSpec) DeepCopyInto(out *ServiceExportsSpec) { - *out = *in - if in.Services != nil { - in, out := &in.Services, &out.Services - *out = make([]ExportedService, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportsSpec. -func (in *ServiceExportsSpec) DeepCopy() *ServiceExportsSpec { - if in == nil { - return nil - } - out := new(ServiceExportsSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceIntentions) DeepCopyInto(out *ServiceIntentions) { *out = *in diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceexports.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceexports.yaml deleted file mode 100644 index c4648703dc..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceexports.yaml +++ /dev/null @@ -1,130 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.0 - creationTimestamp: null - name: serviceexports.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: ServiceExports - listKind: ServiceExportsList - plural: serviceexports - singular: serviceexports - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: ServiceExports is the Schema for the serviceexports 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: ServiceExportsSpec defines the desired state of ServiceExports - properties: - services: - description: Services is a list of services to be exported and the - list of partitions to expose them to. - items: - description: ExportedService manages the exporting of a service - in the local partition to other partitions. - properties: - consumers: - description: Consumers is a list of downstream consumers of - the service to be exported. - items: - description: ServiceConsumer represents a downstream consumer - of the service to be exported. - properties: - partition: - description: Partition is the admin partition to export - the service to. - type: string - type: object - type: array - name: - description: Name is the name of the service to be exported. - type: string - namespace: - description: Namespace is the namespace to export the service - from. - type: string - type: object - type: array - type: object - status: - 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 - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/patches/cainjection_in_serviceexports.yaml b/control-plane/config/crd/patches/cainjection_in_serviceexports.yaml deleted file mode 100644 index ecf0240c53..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_serviceexports.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# 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: serviceexports.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/webhook_in_serviceexports.yaml b/control-plane/config/crd/patches/webhook_in_serviceexports.yaml deleted file mode 100644 index 9f4fc7a30f..0000000000 --- a/control-plane/config/crd/patches/webhook_in_serviceexports.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# 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: serviceexports.consul.hashicorp.com -spec: - conversion: - 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/control-plane/config/rbac/serviceexport_editor_role.yaml b/control-plane/config/rbac/serviceexport_editor_role.yaml deleted file mode 100644 index 9e0ba97a12..0000000000 --- a/control-plane/config/rbac/serviceexport_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit serviceexports. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: serviceexport-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - serviceexports - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - serviceexports/status - verbs: - - get diff --git a/control-plane/config/rbac/serviceexport_viewer_role.yaml b/control-plane/config/rbac/serviceexport_viewer_role.yaml deleted file mode 100644 index a0da087cf8..0000000000 --- a/control-plane/config/rbac/serviceexport_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view serviceexports. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: serviceexport-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - serviceexports - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - serviceexports/status - verbs: - - get diff --git a/control-plane/config/samples/consul_v1alpha1_serviceexport.yaml b/control-plane/config/samples/consul_v1alpha1_serviceexport.yaml deleted file mode 100644 index f1d5414d71..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_serviceexport.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceExport -metadata: - name: serviceexport-sample -spec: - # Add fields here - foo: bar diff --git a/control-plane/controller/serviceexports_controller.go b/control-plane/controller/serviceexports_controller.go deleted file mode 100644 index f18215d5b7..0000000000 --- a/control-plane/controller/serviceexports_controller.go +++ /dev/null @@ -1,40 +0,0 @@ -package controller - -import ( - "context" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -// ServiceExportsController reconciles a ServiceExports object -type ServiceExportsController struct { - client.Client - Log logr.Logger - Scheme *runtime.Scheme - ConfigEntryController *ConfigEntryController -} - -// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=serviceexports,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=serviceexports/status,verbs=get;update;patch - -func (r *ServiceExportsController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.ServiceExports{}) -} - -func (r *ServiceExportsController) Logger(name types.NamespacedName) logr.Logger { - return r.Log.WithValues("request", name) -} - -func (r *ServiceExportsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { - return r.Status().Update(ctx, obj, opts...) -} - -func (r *ServiceExportsController) SetupWithManager(mgr ctrl.Manager) error { - return setupWithManager(mgr, &consulv1alpha1.ServiceExports{}, r) -}