diff --git a/prometheus/cmd/webhook/main.go b/prometheus/cmd/webhook/main.go new file mode 100644 index 0000000000..8b08dcb945 --- /dev/null +++ b/prometheus/cmd/webhook/main.go @@ -0,0 +1,105 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime/schema" + sourcev1alpha1 "knative.dev/eventing-contrib/prometheus/pkg/apis/sources/v1alpha1" + "knative.dev/eventing/pkg/logconfig" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + "knative.dev/pkg/injection/sharedmain" + "knative.dev/pkg/signals" + "knative.dev/pkg/webhook" + "knative.dev/pkg/webhook/certificates" + "knative.dev/pkg/webhook/resourcesemantics" + "knative.dev/pkg/webhook/resourcesemantics/defaulting" + "knative.dev/pkg/webhook/resourcesemantics/validation" +) + +var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ + sourcev1alpha1.SchemeGroupVersion.WithKind("PrometheusSource"): &sourcev1alpha1.PrometheusSource{}, +} + +var callbacks = map[schema.GroupVersionKind]validation.Callback{} + +func NewDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { + return defaulting.NewAdmissionController(ctx, + + // Name of the resource webhook. + "defaulting.webhook.prometheus.sources.knative.dev", + + // The path on which to serve the webhook. + "/defaulting", + + // The resources to validate and default. + types, + + // A function that infuses the context passed to Validate/SetDefaults with custom metadata. + func(ctx context.Context) context.Context { + // Here is where you would infuse the context with state + // (e.g. attach a store with configmap data) + return ctx + }, + + // Whether to disallow unknown fields. + true, + ) +} + +func NewValidationAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { + return validation.NewAdmissionController(ctx, + + // Name of the resource webhook. + "validation.webhook.prometheus.sources.knative.dev", + + // The path on which to serve the webhook. + "/resource-validation", + + // The resources to validate and default. + types, + + // A function that infuses the context passed to Validate/SetDefaults with custom metadata. + func(ctx context.Context) context.Context { + // Here is where you would infuse the context with state + // (e.g. attach a store with configmap data) + return ctx + }, + + // Whether to disallow unknown fields. + true, + + // Extra validating callbacks to be applied to resources. + callbacks, + ) +} + +func main() { + ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{ + ServiceName: logconfig.WebhookName(), + Port: 8443, + SecretName: "prometheus-source-webhook-certs", + }) + + sharedmain.WebhookMainWithContext(ctx, logconfig.WebhookName(), + certificates.NewController, + NewDefaultingAdmissionController, + NewValidationAdmissionController, + ) +} diff --git a/prometheus/config/200-serviceaccount.yaml b/prometheus/config/200-serviceaccount.yaml index ef6d4fd0d7..30455d367a 100644 --- a/prometheus/config/200-serviceaccount.yaml +++ b/prometheus/config/200-serviceaccount.yaml @@ -17,3 +17,13 @@ kind: ServiceAccount metadata: name: prometheus-controller-manager namespace: knative-sources + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: prometheus-source-webhook + namespace: knative-sources + labels: + contrib.eventing.knative.dev/release: devel diff --git a/prometheus/config/202-clusterrolebinding.yaml b/prometheus/config/202-clusterrolebinding.yaml index e0a1834cbf..16b2e3f4bc 100644 --- a/prometheus/config/202-clusterrolebinding.yaml +++ b/prometheus/config/202-clusterrolebinding.yaml @@ -42,3 +42,19 @@ roleRef: kind: ClusterRole name: addressable-resolver +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: eventing-sources-prometheus-webhook + labels: + contrib.eventing.knative.dev/release: devel +subjects: + - kind: ServiceAccount + name: prometheus-source-webhook + namespace: knative-sources +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus-source-webhook diff --git a/prometheus/config/203-webhook-clusterrole.yaml b/prometheus/config/203-webhook-clusterrole.yaml new file mode 100644 index 0000000000..3a744b54e7 --- /dev/null +++ b/prometheus/config/203-webhook-clusterrole.yaml @@ -0,0 +1,122 @@ +# Copyright 2020 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: prometheus-source-webhook + labels: + contrib.eventing.knative.dev/release: devel +rules: + # Sources admin + - apiGroups: + - sources.knative.dev + resources: + - prometheussources + verbs: &everything + - get + - list + - watch + - create + - update + - patch + - delete + + # Sources finalizer + - apiGroups: + - sources.knative.dev + resources: + - prometheussources/finalizers + verbs: *everything + + # Source statuses update + - apiGroups: + - sources.knative.dev + resources: + - prometheussources/status + verbs: + - get + - update + - patch + + # Deployments admin + - apiGroups: + - apps + resources: + - deployments + verbs: *everything + + # Knative Services admin + - apiGroups: + - serving.knative.dev + resources: + - services + verbs: *everything + + # Secrets read + - apiGroups: + - "" + resources: + - secrets + - services + verbs: + - get + - list + - watch + + # Namespace labelling for webhook + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - patch + + # Events admin + - apiGroups: + - "" + resources: + - events + - configmaps + verbs: *everything + + # EventTypes admin + - apiGroups: + - eventing.knative.dev + resources: + - eventtypes + verbs: *everything + + # For manipulating certs into secrets. + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "create" + - "update" + - "list" + - "watch" + + # For actually registering our webhook. + - apiGroups: + - "admissionregistration.k8s.io" + resources: + - "mutatingwebhookconfigurations" + - "validatingwebhookconfigurations" + verbs: *everything diff --git a/prometheus/config/500-webhook-config.yaml b/prometheus/config/500-webhook-config.yaml new file mode 100644 index 0000000000..9ab68d4f16 --- /dev/null +++ b/prometheus/config/500-webhook-config.yaml @@ -0,0 +1,45 @@ +# Copyright 2020 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: defaulting.webhook.prometheus.sources.knative.dev + labels: + contrib.eventing.knative.dev/release: devel +webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: prometheus-source-webhook + namespace: knative-sources + failurePolicy: Fail + name: defaulting.webhook.prometheus.sources.knative.dev +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validation.webhook.prometheus.sources.knative.dev + labels: + contrib.eventing.knative.dev/release: devel +webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: prometheus-source-webhook + namespace: knative-sources + failurePolicy: Fail + name: validation.webhook.prometheus.sources.knative.dev diff --git a/prometheus/config/500-webhook.yaml b/prometheus/config/500-webhook.yaml new file mode 100644 index 0000000000..5cadf3ac22 --- /dev/null +++ b/prometheus/config/500-webhook.yaml @@ -0,0 +1,80 @@ +# Copyright 2020 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: Secret +metadata: + name: prometheus-source-webhook-certs + namespace: knative-sources + labels: + contrib.eventing.knative.dev/release: devel +# The data is populated at install time. + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prometheus-source-webhook + namespace: knative-sources + labels: + contrib.eventing.knative.dev/release: devel +spec: + replicas: 1 + selector: + matchLabels: &labels + app: prometheus-source-webhook + role: prometheus-source-webhook + template: + metadata: + annotations: + sidecar.istio.io/inject: "false" + labels: *labels + spec: + serviceAccountName: prometheus-source-webhook + containers: + - name: prometheus-source-webhook + terminationMessagePolicy: FallbackToLogsOnError + image: ko://knative.dev/eventing-contrib/prometheus/cmd/webhook + env: + - name: SYSTEM_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIG_LOGGING_NAME + value: config-logging + - name: METRICS_DOMAIN + value: knative.dev/eventing + - name: WEBHOOK_NAME + value: prometheus-source-webhook + ports: + - containerPort: 9090 + name: metrics + # TODO set proper resource limits. +--- +apiVersion: v1 +kind: Service +metadata: + labels: + eventing.knative.dev/release: devel + role: prometheus-source-webhook + name: prometheus-source-webhook + namespace: knative-sources +spec: + ports: + - name: https-webhook + port: 443 + targetPort: 8443 + selector: + role: prometheus-source-webhook diff --git a/prometheus/pkg/apis/sources/v1alpha1/prometheus_defaults.go b/prometheus/pkg/apis/sources/v1alpha1/prometheus_defaults.go new file mode 100644 index 0000000000..0eee1869ac --- /dev/null +++ b/prometheus/pkg/apis/sources/v1alpha1/prometheus_defaults.go @@ -0,0 +1,29 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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" +) + +func (s *PrometheusSource) SetDefaults(ctx context.Context) { + s.Spec.SetDefaults(ctx) +} + +func (s *PrometheusSourceSpec) SetDefaults(ctx context.Context) { + // Nothing yet. +} diff --git a/prometheus/pkg/apis/sources/v1alpha1/prometheus_defaults_test.go b/prometheus/pkg/apis/sources/v1alpha1/prometheus_defaults_test.go new file mode 100644 index 0000000000..75c04c152f --- /dev/null +++ b/prometheus/pkg/apis/sources/v1alpha1/prometheus_defaults_test.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestPrometheusSourceDefaults(t *testing.T) { + testCases := map[string]struct { + initial PrometheusSource + expected PrometheusSource + }{ + "nil spec": { + initial: PrometheusSource{}, + expected: PrometheusSource{ + Spec: PrometheusSourceSpec{}, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + tc.initial.SetDefaults(context.TODO()) + if diff := cmp.Diff(tc.expected, tc.initial); diff != "" { + t.Fatalf("Unexpected defaults (-want, +got): %s", diff) + } + }) + } +} diff --git a/prometheus/pkg/apis/sources/v1alpha1/prometheus_validation.go b/prometheus/pkg/apis/sources/v1alpha1/prometheus_validation.go new file mode 100644 index 0000000000..1f4a7a3c65 --- /dev/null +++ b/prometheus/pkg/apis/sources/v1alpha1/prometheus_validation.go @@ -0,0 +1,42 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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" + + "knative.dev/pkg/apis" +) + +// Validate Prometheus source object fields +func (s *PrometheusSource) Validate(ctx context.Context) *apis.FieldError { + return s.Spec.Validate(ctx).ViaField("spec") +} + +// Validate Prometheus source Spec object fields +func (s *PrometheusSourceSpec) Validate(ctx context.Context) *apis.FieldError { + var errs *apis.FieldError + + // Validate sink + if s.Sink == nil { + fe := apis.ErrMissingField("sink") + errs = errs.Also(fe) + } else if fe := s.Sink.Validate(ctx); fe != nil { + errs = errs.Also(fe.ViaField("sink")) + } + return errs +} diff --git a/prometheus/pkg/apis/sources/v1alpha1/prometheus_validation_test.go b/prometheus/pkg/apis/sources/v1alpha1/prometheus_validation_test.go new file mode 100644 index 0000000000..821cd1144f --- /dev/null +++ b/prometheus/pkg/apis/sources/v1alpha1/prometheus_validation_test.go @@ -0,0 +1,55 @@ +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES 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" + "testing" + + "github.com/google/go-cmp/cmp" + "knative.dev/pkg/webhook/resourcesemantics" + + "knative.dev/pkg/apis" +) + +func TestPrometheusSourceValidation(t *testing.T) { + testCases := map[string]struct { + cr resourcesemantics.GenericCRD + want *apis.FieldError + }{ + "missing sink": { + cr: &PrometheusSource{ + Spec: PrometheusSourceSpec{}, + }, + want: func() *apis.FieldError { + var errs *apis.FieldError + fe := apis.ErrMissingField("spec.sink") + errs = errs.Also(fe) + return errs + }(), + }, + } + + for n, test := range testCases { + t.Run(n, func(t *testing.T) { + got := test.cr.Validate(context.Background()) + if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { + t.Errorf("%s: validate (-want, +got) = %v", n, diff) + } + }) + } +} diff --git a/prometheus/pkg/apis/sources/v1alpha1/prometheussource_types.go b/prometheus/pkg/apis/sources/v1alpha1/prometheussource_types.go index 052e15c983..fa55743f89 100644 --- a/prometheus/pkg/apis/sources/v1alpha1/prometheussource_types.go +++ b/prometheus/pkg/apis/sources/v1alpha1/prometheussource_types.go @@ -23,6 +23,7 @@ import ( "knative.dev/pkg/apis/duck" duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/pkg/kmeta" + "knative.dev/pkg/webhook/resourcesemantics" ) // +genclient @@ -39,6 +40,8 @@ type PrometheusSource struct { Status PrometheusSourceStatus `json:"status,omitempty"` } +var _ resourcesemantics.GenericCRD = (*PrometheusSource)(nil) + // Check that Prometheus source can be validated and can be defaulted. var _ runtime.Object = (*PrometheusSource)(nil)